Skip to content

Commit

Permalink
Refactor and test character movement
Browse files Browse the repository at this point in the history
  • Loading branch information
Parnswir committed Dec 6, 2017
1 parent 2646cde commit ac49c2d
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 32 deletions.
5 changes: 5 additions & 0 deletions src/direction.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ export default class Direction {
}
}

static getModifier (pos, target) {
const _mod = (axis) => (target[axis] - pos[axis]) / Math.abs(target[axis] - pos[axis]);
return {x: _mod('x') || 0, y: _mod('y') || 0};
}

}
73 changes: 41 additions & 32 deletions src/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export default class Player extends Animated(EventEmitting()) {
super(properties);
this.properties = properties;
this.tile = {x, y};
this.previousTile = this.tile;
this.pos = {
x: this.tile.x * this.properties.tileWidth + this.texture.width / 2,
y: this.tile.y * this.properties.tileHeight + this.texture.height / 2
Expand Down Expand Up @@ -109,11 +108,6 @@ export default class Player extends Animated(EventEmitting()) {
this.context.drawImage(this.getFrame(this.properties.files, this.direction), getFrameX(offset), getFrameY(offset));
}

get previousTile () {return this._previousTile}
set previousTile (tile) {
this._previousTile = tile;
}

get hadPath () {return this._hadPath || false}
set hadPath (value) {
this._hadPath = value;
Expand All @@ -139,37 +133,52 @@ export default class Player extends Animated(EventEmitting()) {
};
}

move () {
updateTile ({x, y}) {
const nextTile = this.properties.layer.getXYCoords(x, y);
if (this.tile.x !== nextTile.x || this.tile.y !== nextTile.y) {
this.createEvent("changeTile", nextTile);
this.tile = nextTile;
}
}

_moveInPath () {
if (this.inPosition.x && this.inPosition.y) {
this.path.shift();
this.createEvent("pathComplete", this.path);
} else {
this._moveTowardsTarget();
}
}

_moveTowardsTarget () {
const speed = this.properties.speed;
const modifier = Direction.getModifier(this.pos, this.target);
this.pos = {
x: this.pos.x + modifier.x * speed,
y: this.pos.y + modifier.y * speed
};
const nextTile = {
x: this.tile.x + modifier.x,
y: this.tile.y + modifier.y
};
this.direction = Direction.from(this.tile, nextTile);
}

_completeMovement () {
if (this.hadPath) {
this.createEvent("movementComplete");
this.hadPath = false;
}
}

move () {
if (this.path.length > 0) {
this.hadPath = true;
this.stepAnimation();

if (this.inPosition.x && this.inPosition.y) {
this.path.shift();
this.createEvent("pathComplete", this.path);
} else {
if (!this.inPosition.x) {
const modifier = (this.target.x - this.pos.x) / Math.abs(this.target.x - this.pos.x);
this.direction = modifier > 0 ? Direction.RIGHT : Direction.LEFT;
this.pos.x += modifier * speed;
}
if (!this.inPosition.y) {
const modifier = (this.target.y - this.pos.y) / Math.abs(this.target.y - this.pos.y);
this.direction = modifier > 0 ? Direction.DOWN : Direction.UP;
this.pos.y += modifier * speed;
}
}
this._moveInPath();
} else {
if (this.hadPath) {
this.createEvent("movementComplete");
this.hadPath = false;
}
}
this.tile = this.properties.layer.getXYCoords(this.pos.x, this.pos.y);
if (this.tile.x !== this.previousTile.x || this.tile.y !== this.previousTile.y) {
this.createEvent("changeTile", this.tile);
this.previousTile = this.tile;
this._completeMovement();
}
this.updateTile(this.pos);
}
}
15 changes: 15 additions & 0 deletions test/direction.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,18 @@ test('#tileInDirection works for specified step', t => {
t.deepEqual(Direction.tileInDirection(P, Direction.DOWN, 8), {x: P.x, y: P.y + 8});
t.deepEqual(Direction.tileInDirection(P, Direction.UP, 6), {x: P.x, y: P.y - 6});
});

test('#getModifier determines on which axis a target is situated', t => {
const P = {x: 5, y: 7};
t.deepEqual(Direction.getModifier(P, P), {x: 0, y: 0});

t.deepEqual(Direction.getModifier(P, {x: 5, y: 8}), {x: 0, y: 1});
t.deepEqual(Direction.getModifier(P, {x: 9, y: 7}), {x: 1, y: 0});
t.deepEqual(Direction.getModifier(P, {x: 5, y: 4}), {x: 0, y: -1});
t.deepEqual(Direction.getModifier(P, {x: 1, y: 7}), {x: -1, y: 0});

t.deepEqual(Direction.getModifier(P, {x: 6, y: 8}), {x: 1, y: 1});
t.deepEqual(Direction.getModifier(P, {x: 2, y: 9}), {x: -1, y: 1});
t.deepEqual(Direction.getModifier(P, {x: 6, y: 4}), {x: 1, y: -1});
t.deepEqual(Direction.getModifier(P, {x: 3, y: 4}), {x: -1, y: -1});
});
94 changes: 94 additions & 0 deletions test/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,97 @@ test('#inPosition works for set targets', t => {
t.truthy(player.inPosition.x);
t.truthy(player.inPosition.y);
});

test('#_completeMovement emits an event if a path was completed', t => {
const player = new Player({}, {}, 3, 6);
t.plan(2);
player.on('movementComplete', () => t.pass());
player._completeMovement(); // should not send event
player.hadPath = true;
player._completeMovement(); // should send event
t.falsy(player.hadPath);
});

test('#_moveTowardsTarget moves the player by [speed] pixels into the direction of the target', t => {
const SPEED = 3;
const player = new Player({}, {speed: SPEED}, 3, 6);
const startPos = player.pos;

player.target = {x: 3, y: 7};
player._moveTowardsTarget();

t.is(player.pos.y, startPos.y + SPEED);
});

test('#_moveTowardsTarget lets the player look into the direction of the target', t => {
const player = new Player({}, {}, 5, 3);
player.direction = Direction.UP;

player.target = {x: 3, y: 3};
player._moveTowardsTarget();

t.is(player.direction, Direction.LEFT);
});

test('#_moveInPath moves towards the first path element', t => {
const player = new Player({}, {}, 5, 3);
const startPos = player.pos;
player.path = [{x: 5, y: 2}, {x: 4, y: 2}];
player._moveInPath();
t.is(player.pos.y, startPos.y - 1);
});

test('#_moveInPath emits an event if it´s path is finished', t => {
const player = new Player({}, {}, 5, 3);
const startPos = player.pos;
t.plan(2);
player.on('pathComplete', () => t.pass());
player.path = [];
player._moveInPath();
t.deepEqual(player.pos, startPos);
});

test('#_moveInPath emits an event if it´s target is reached', t => {
t.plan(3);
const player = new Player({}, {}, 6, 7);
const startPos = player.pos;
const [FIRST, SECOND] = [player.tile, {x: 2, y: 6}];
player.on('pathComplete', (p, newPath) => {
t.is(p, player);
t.deepEqual(newPath, [SECOND]);
});
player.path = [FIRST, SECOND];
player._moveInPath();
t.deepEqual(player.pos, startPos);
});

test('#updateTile changes the tile according to the new position', t => {
const player = new Player({}, {
layer: {
getXYCoords: (x, y) => {
return {x: Math.floor(x / 32), y: Math.floor(y / 32)}
}
}
}, 6, 7);
const startTile = player.tile;
player.updateTile({x: player.pos.x + 32, y: player.pos.y});
t.notDeepEqual(player.tile, startTile);
t.deepEqual(player.tile, {x: 7, y: 7});
});

test('#updateTile emits an event when actually changing the tile', t => {
const TILE = {x: 1, y: 2};
const player = new Player({}, {
layer: {
getXYCoords: (x, y) => TILE
}
}, 6, 7);
const startTile = player.tile;
player.on('changeTile', (p, newTile) => {
t.is(p, player);
t.deepEqual(newTile, TILE);
});
t.plan(2);
player.updateTile({x: 1, y: 1});
player.updateTile({x: 1, y: 1});
});

0 comments on commit ac49c2d

Please sign in to comment.