Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Прошу совета по коду игры (https://javascript.ru/forum/project/70476-proshu-soveta-po-kodu-igry.html)

malinovsky 12.09.2017 11:15

Rise,
Цитата:

надо на классах делать
как здесь: https://www.youtube.com/watch?v=ju09womACpQ ?

Rise 13.09.2017 19:25

malinovsky,
да, но возможно там не совсем очевидны преимущества ООП подхода в общем и классов в частности, давай я тебе покажу:
<canvas width="600" height="400"></canvas>

<script>

class Game {
	constructor(canvas) {
		canvas.onmouseenter = () => this.start();
		canvas.onmouseleave = () => this.pause();
		this.ctx2d = canvas.getContext('2d');
		this.ctx2d.fillText('START', 0, 10);
		this.world = new Set();
		this._last = 0;
		this._step = (now) => {
			this._loop = requestAnimationFrame(this._step);
			this.delta = Math.min(now - this._last, 100) / 1000;
			this._last = now;
			this.update();
			this.render();
		};
	}
	start() {
		if (!this._loop) this._loop = requestAnimationFrame(this._step);
	}
	pause() {
		if (this._loop) this._loop = cancelAnimationFrame(this._loop);
	}
	collide(entity1, type) {
		for (let entity2 of this.world) {
			if (entity1 != entity2  && 
				entity2.type & type && 
				entity1.x < entity2.x + entity2.w &&
				entity1.x + entity1.w > entity2.x &&
				entity1.y < entity2.y + entity2.h &&
				entity1.h + entity1.y > entity2.y) return true;
		}
		return false;
	}
	update() {
		for (let entity of this.world) if (entity.update) entity.update(this);
	}
	render() {
		this.ctx2d.clearRect(0, 0, 600, 400);
		for (let entity of this.world) if (entity.render) entity.render(this);
		if (!this.world.size) this.pause();
	}
}

class StaticBase {
	constructor(statics) {
		Object.assign(this, { x: 0, y: 0, w: 10, h: 10, c: 'black' }, statics);
	}
	render(game) {
		game.ctx2d.fillStyle = this.c;
		game.ctx2d.fillRect(this.x, this.y, this.w, this.h);
	}
}
class DynamicBase extends StaticBase {
	constructor(statics, dynamics) {
		super(statics);
		Object.assign(this, { vx: 0, vy: 0 }, dynamics);
	}
	update(game) {
		this.x += this.vx * game.delta;
		this.y += this.vy * game.delta;
	}
}
class SinusoidBase extends DynamicBase {
	constructor(statics, sinusoid) {
		super(statics, null);
		Object.assign(this, { A: 0, B: 0, C: 0, D: 0, E: 0, F: 0, G: 0, H: 0, t: 0 }, sinusoid);
	}
	update(game) {
		this.t += game.delta;
		this.vx = this.A + this.B * Math.sin(this.C * this.t + this.D);
		this.vy = this.E + this.F * Math.sin(this.G * this.t + this.H);
		super.update(game);
	}
}

const PLAYER = 1, SHIP = 2, SHOT = 4, LINE = 8;

class Line extends StaticBase {
	constructor(statics, extra) {
		super(statics);
		Object.assign(this, { type: LINE }, extra);
	}
}
class Shot extends DynamicBase {
	constructor(statics, dynamics, extra) {
		super(statics, dynamics);
		Object.assign(this, { type: SHOT }, extra);
	}
	update(game) {
		super.update(game);
		if (game.collide(this, PLAYER | SHOT | LINE)) game.world.delete(this);
	}
}
class Ship extends SinusoidBase {
	constructor(statics, sinusoid, extra) {
		super(statics, sinusoid);
		Object.assign(this, { type: SHIP }, extra);
	}
	update(game) {
		super.update(game);
		if (game.collide(this, PLAYER | SHOT | LINE)) game.world.delete(this);
	}
}

const theGame = new Game(document.querySelector('canvas'));

theGame.world
.add(new Line({ x:   0, y:   5, w:   5, h: 375 }))
.add(new Line({ x:   0, y:   0, w: 600, h:   5 }))
.add(new Line({ x: 595, y:   5, w:   5, h: 375 }))
.add(new Line({ x:   0, y: 380, w: 600, h:  20 }))
.add(new Shot({ x: 100, y: 370, w:   5, h:   5 }, { vy: -50 }))
.add(new Shot({ x: 400, y: 370, w:   5, h:   5 }, { vy: -20 }))
.add(new Shot({ x: 400, y:   5, w:   5, h:   5 }, { vy:  40 }))
.add(new Ship({ x: 100, y:   5, c: 'gray'  }, { E:   20 }))
.add(new Ship({ x:   5, y: 100, c: 'brown' }, { A:   30 }))
.add(new Ship({ x: 410, y:  10, c: 'orange'}, { B: -200, C: 1, E: 20, F: 100, G: 2, H: 1.5 }))
.add(new Ship({ x: 580, y:  60, c: 'red'   }, { B: -100, C: 1, E: 10, F: 100, G: 1, H: 1.5 }))
.add(new Ship({ x: 200, y:   5, c: 'green' }, { B:  100, C: 4, E: 30 }))
.add(new Ship({ x: 280, y:   5, c: 'blue'  }, { B:  300, C: 2, E: 20 }))

</script>

Как видишь теперь разрабатывать удобней и понятней, и не составит труда придумывать новые проекты (классы) сущностей на основе базовых или более сложных и заселять ими игровой мир. И чем сложнее разработка тем эти преимущества острее чувствуются, потому что человеку по природе естественно размышлять объектами.

рони 13.09.2017 20:04

Rise,
спасибо за науку!!! :thanks:

malinovsky 14.09.2017 21:00

Rise,
Спасибо! Буду изучать)

Rise 16.09.2017 00:06

Стоит еще отметить, что это PLAYER | SHOT | LINE - битовая маска, до 32 констант можно создать и комбинировать их в разные маски, что весьма удобно:
const TYPE_NAME01 = 1 << 0, // ...0000000001 (2)   1 (10)
      TYPE_NAME02 = 1 << 1, // ...0000000010 (2)   2 (10)
      TYPE_NAME03 = 1 << 2, // ...0000000100 (2)   4 (10)
      TYPE_NAME04 = 1 << 3, // ...0000001000 (2)   8 (10)
      TYPE_NAME05 = 1 << 4, // ...0000010000 (2)  16 (10)
      TYPE_NAME06 = 1 << 5, // ...0000100000 (2)  32 (10)
      TYPE_NAME07 = 1 << 6, // ...0001000000 (2)  64 (10)
      TYPE_NAME08 = 1 << 7, // ...0010000000 (2) 128 (10)
      TYPE_NAME09 = 1 << 8, // ...0100000000 (2) 256 (10)
      TYPE_NAME10 = 1 << 9, // ...1000000000 (2) 512 (10)
   // ...           ...        ...
   // TYPE_NAME32 = 1 << 31,// ...10000000000000000000000000000000 (2) 2147483648 (10)
      TYPE_GROUP1 = TYPE_NAME01 | TYPE_NAME02 | TYPE_NAME03 | TYPE_NAME04 | TYPE_NAME05, // ...0000011111 (2)  31 (10)
      TYPE_GROUP2 = TYPE_NAME06 | TYPE_NAME07 | TYPE_NAME08 | TYPE_NAME09 | TYPE_NAME10, // ...1111100000 (2) 992 (10)
      TYPE_ALL    = TYPE_GROUP1 | TYPE_GROUP2 ; // ...1111111111 (2) 1023 (10)


demo(TYPE_ALL, 'all');
demo(TYPE_GROUP1, 'group 1');
demo(TYPE_GROUP2, 'group 2');
demo(TYPE_ALL ^ TYPE_NAME03 ^ TYPE_NAME08, '(^) all types except 3 and 8');             // ...1101111011 (2) 891 (10)
demo(TYPE_ALL & ~TYPE_NAME03 & ~TYPE_NAME08, '(& ~) all types except 3 and 8');         // 
demo(TYPE_NAME03 | TYPE_GROUP2 ^ TYPE_NAME08, 'only type 3 and group 2 except type 8'); // ...1101100100 (2) 868 (10)

function demo(type, note) {
    const mask = [null, null, null, null, null, null, null, null, null, null];
    if (type & TYPE_NAME01) mask[9] = '01';
    if (type & TYPE_NAME02) mask[8] = '02';
    if (type & TYPE_NAME03) mask[7] = '03';
    if (type & TYPE_NAME04) mask[6] = '04';
    if (type & TYPE_NAME05) mask[5] = '05';
    if (type & TYPE_NAME06) mask[4] = '06';
    if (type & TYPE_NAME07) mask[3] = '07';
    if (type & TYPE_NAME08) mask[2] = '08';
    if (type & TYPE_NAME09) mask[1] = '09';
    if (type & TYPE_NAME10) mask[0] = '10';
    console.log(mask, note);
}

рони 16.09.2017 00:28

Rise,
:write:

j0hnik 16.09.2017 01:02

Цитата:

Сообщение от рони (Сообщение 464620)
Rise,
:write:

Для меня пока что это темный лес :(

рони 16.09.2017 01:40

j0hnik,
маска

malinovsky 18.09.2017 00:45

Rise,
рони,
Большое Вам спасибо, что помогаете новичкам. :thanks:
Начал разбирать примеры с ООП и осознал - нужно учить ООП.:-?
Вот например, почему это работает:
class Game 
{
	constructor()
	{
		this.loop = (time) => {
			console.log(time);
			requestAnimationFrame(this.loop);
		};

		this.loop();

	}
}
var game = new Game();

а это нет:
class Game 
{
	constructor()
	{
		this.loop = function(time) {
			console.log(time);
			requestAnimationFrame(this.loop);
		};

		this.loop();

	}
}
var game = new Game();


Почему в стрелочных функциях this ведет себя не так как в обычных?
Совсем запутался.:blink:

malinovsky 18.09.2017 00:56

Взялся, блин, делать игры, математику не понимаю, ООП не знаю. В GameDev меня ждет большое будущее...:-E


Часовой пояс GMT +3, время: 10:21.