Pause log processing during ship and weapon animations
This commit is contained in:
parent
5b7b01d85e
commit
8cb165e1a4
19
TODO
19
TODO
|
@ -1,9 +1,13 @@
|
|||
* Ensure that tweens and particle emitters get destroyed once animation is done
|
||||
* Stop processing the log during a ship move animation, or a weapon animation
|
||||
* Highlight ships that will be included as target of current action
|
||||
* Fix action tooltip sometimes not being hidden when the mouse goes out of action icon
|
||||
* Quick loading does not cancel pending "setTimeout"s.
|
||||
* Drones: add tooltip
|
||||
* Drones: add hull points and take area damage
|
||||
* Drones: change the sprite angle for deploy animation
|
||||
* Drones: add animation for each activation
|
||||
* Drones: fix not being removed when owner is in statis (owner's turn is skipped)
|
||||
* Do not apply effects on ships in stasis
|
||||
* Add a battle log display
|
||||
* Organize arena objects and information in layers
|
||||
* Prevent arena effects information (eg. "shield -36") to overflow out of the arena
|
||||
|
@ -23,13 +27,14 @@
|
|||
* Mobile: targetting in two times, using a draggable target indicator
|
||||
* AI: apply safety distances to move actions
|
||||
* AI: bully AI crashes when winning a battle (trying to move toward null ship!)
|
||||
* AI: sometimes faces a cardinal point, then is stuck in an infinite thinking loop
|
||||
* Add a defeat screen (game over for now)
|
||||
* Add a victory screen, with loot display
|
||||
* Add retreat from battle
|
||||
* Map : restore fog of war
|
||||
* Map : add information on current star/location + information on hovered location
|
||||
* Map : add stores and shipyards
|
||||
* Map : remove jump links that cross the radius of other systems
|
||||
* Map : improve performance
|
||||
* Menu : fix background stars aggregating at right side when the game is not focused
|
||||
* Map: restore fog of war
|
||||
* Map: add information on current star/location + information on hovered location
|
||||
* Map: add stores and shipyards
|
||||
* Map: remove jump links that cross the radius of other systems
|
||||
* Map: improve performance
|
||||
* Menu: fix background stars aggregating at right side when the game is not focused
|
||||
* Multiplayer
|
|
@ -248,7 +248,9 @@ module TS.SpaceTac {
|
|||
battle.drones = [drone];
|
||||
battle.log.events = [];
|
||||
battle.injectInitialEvents();
|
||||
expect(battle.log.events).toEqual([new DroneDeployedEvent(drone)]);
|
||||
let expected = new DroneDeployedEvent(drone);
|
||||
expected.initial = true;
|
||||
expect(battle.log.events).toEqual([expected]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -234,18 +234,24 @@ module TS.SpaceTac {
|
|||
var log = this.log;
|
||||
|
||||
// Simulate initial ship placement
|
||||
this.play_order.forEach((ship: Ship) => {
|
||||
log.add(new MoveEvent(ship, ship.arena_x, ship.arena_y));
|
||||
this.play_order.forEach(ship => {
|
||||
let event = new MoveEvent(ship, ship.arena_x, ship.arena_y);
|
||||
event.initial = true;
|
||||
log.add(event);
|
||||
});
|
||||
|
||||
// Simulate drones deployment
|
||||
this.drones.forEach(drone => {
|
||||
log.add(new DroneDeployedEvent(drone));
|
||||
let event = new DroneDeployedEvent(drone);
|
||||
event.initial = true;
|
||||
log.add(event);
|
||||
});
|
||||
|
||||
// Simulate game turn
|
||||
if (this.playing_ship) {
|
||||
log.add(new ShipChangeEvent(this.playing_ship, this.playing_ship));
|
||||
let event = new ShipChangeEvent(this.playing_ship, this.playing_ship);
|
||||
event.initial = true;
|
||||
log.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,10 +277,10 @@ module TS.SpaceTac {
|
|||
*/
|
||||
addDrone(drone: Drone, log = true) {
|
||||
if (add(this.drones, drone)) {
|
||||
drone.onDeploy(this.play_order);
|
||||
if (log) {
|
||||
this.log.add(new DroneDeployedEvent(drone));
|
||||
}
|
||||
drone.onDeploy(this.play_order);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -355,20 +355,35 @@ module TS.SpaceTac {
|
|||
return distance <= radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the ship in place to face a direction
|
||||
*/
|
||||
rotate(angle: number, log = true) {
|
||||
if (angle != this.arena_angle) {
|
||||
this.setArenaFacingAngle(angle);
|
||||
|
||||
if (log) {
|
||||
this.addBattleEvent(new MoveEvent(this, this.arena_x, this.arena_y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move toward a location
|
||||
// This does not check or consume action points
|
||||
moveTo(x: number, y: number, log: boolean = true): void {
|
||||
var angle = Math.atan2(y - this.arena_y, x - this.arena_x);
|
||||
this.setArenaFacingAngle(angle);
|
||||
if (x != this.arena_x || y != this.arena_y) {
|
||||
var angle = Math.atan2(y - this.arena_y, x - this.arena_x);
|
||||
this.setArenaFacingAngle(angle);
|
||||
|
||||
this.setArenaPosition(x, y);
|
||||
this.setArenaPosition(x, y);
|
||||
|
||||
if (log) {
|
||||
this.addBattleEvent(new MoveEvent(this, x, y));
|
||||
if (log) {
|
||||
this.addBattleEvent(new MoveEvent(this, x, y));
|
||||
}
|
||||
|
||||
// Broadcast to drones
|
||||
this.forEachDrone(drone => drone.onShipMove(this));
|
||||
}
|
||||
|
||||
// Broadcast to drones
|
||||
this.forEachDrone(drone => drone.onShipMove(this));
|
||||
}
|
||||
|
||||
// Set the death status on this ship
|
||||
|
|
|
@ -39,6 +39,9 @@ module TS.SpaceTac {
|
|||
var affected: Ship[] = [];
|
||||
var blast = this.getBlastRadius(ship);
|
||||
|
||||
// Face the target
|
||||
ship.rotate(Target.newFromShip(ship).getAngleTo(target));
|
||||
|
||||
// Collect affected ships
|
||||
if (blast) {
|
||||
affected = affected.concat(battle.collectShipsInCircle(target, blast));
|
||||
|
|
|
@ -8,10 +8,10 @@ module TS.SpaceTac.Specs {
|
|||
TestTools.setShipAP(ship, 100);
|
||||
TestTools.setShipHP(ship, 50, 30);
|
||||
var enemy1 = battle.fleets[1].ships[0];
|
||||
enemy1.setArenaPosition(0, 1);
|
||||
enemy1.setArenaPosition(1, 0);
|
||||
TestTools.setShipHP(enemy1, 50, 30);
|
||||
var enemy2 = battle.fleets[1].ships[1];
|
||||
enemy2.setArenaPosition(0, 2);
|
||||
enemy2.setArenaPosition(2, 0);
|
||||
TestTools.setShipHP(enemy2, 50, 30);
|
||||
|
||||
var template = new Equipments.SubMunitionMissile();
|
||||
|
@ -47,7 +47,7 @@ module TS.SpaceTac.Specs {
|
|||
battle.log.clear();
|
||||
|
||||
// Fire in space
|
||||
t = Target.newFromLocation(0, 2.4);
|
||||
t = Target.newFromLocation(2.4, 0);
|
||||
expect(equipment.action.canBeUsed(battle, ship)).toBe(true);
|
||||
equipment.action.apply(battle, ship, t);
|
||||
checkHP(50, 10, 40, 0, 40, 0);
|
||||
|
@ -59,7 +59,7 @@ module TS.SpaceTac.Specs {
|
|||
battle.log.clear();
|
||||
|
||||
// Fire far away
|
||||
t = Target.newFromLocation(0, 5);
|
||||
t = Target.newFromLocation(5, 0);
|
||||
expect(equipment.action.canBeUsed(battle, ship)).toBe(true);
|
||||
equipment.action.apply(battle, ship, t);
|
||||
checkHP(50, 10, 40, 0, 40, 0);
|
||||
|
|
|
@ -10,6 +10,9 @@ module TS.SpaceTac {
|
|||
// Target of the event
|
||||
target: Target;
|
||||
|
||||
// Boolean at true if the event is used to set initial battle conditions
|
||||
initial = false;
|
||||
|
||||
constructor(code: string, ship: Ship = null, target: Target = null) {
|
||||
this.code = code;
|
||||
this.ship = ship;
|
||||
|
|
|
@ -98,7 +98,7 @@ module TS.SpaceTac.UI {
|
|||
var sprite = this.findShipSprite(ship);
|
||||
if (sprite) {
|
||||
sprite.alpha = 0.5;
|
||||
sprite.displayEffect("Emergency Stasis", false);
|
||||
sprite.displayEffect("stasis", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,8 +139,12 @@ module TS.SpaceTac.UI {
|
|||
this.battleview.gameui.audio.playOnce("battle-ship-change");
|
||||
}
|
||||
|
||||
// Spawn a new drone
|
||||
addDrone(drone: Drone): void {
|
||||
/**
|
||||
* Spawn a new drone
|
||||
*
|
||||
* Return the duration of deploy animation
|
||||
*/
|
||||
addDrone(drone: Drone): number {
|
||||
if (!any(this.drone_sprites, sprite => sprite.drone == drone)) {
|
||||
let sprite = new ArenaDrone(this.battleview, drone);
|
||||
this.addChild(sprite);
|
||||
|
@ -148,8 +152,12 @@ module TS.SpaceTac.UI {
|
|||
|
||||
sprite.position.set(drone.owner.arena_x, drone.owner.arena_y);
|
||||
this.game.tweens.create(sprite.position).to({ x: drone.x, y: drone.y }, 1800, Phaser.Easing.Sinusoidal.InOut, true, 200);
|
||||
this.game.tweens.create(sprite.radius.scale).from({ x: 0.01, y: 0.01 }, 1800, Phaser.Easing.Linear.None, true, 200);
|
||||
|
||||
return 2000;
|
||||
} else {
|
||||
console.error("Drone added twice to arena", drone);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ module TS.SpaceTac.UI {
|
|||
// Effects display
|
||||
effects: Phaser.Group;
|
||||
|
||||
// Previous position
|
||||
private prevx;
|
||||
private prevy;
|
||||
|
||||
// Create a ship sprite usable in the Arena
|
||||
constructor(parent: Arena, ship: Ship) {
|
||||
super(parent.game);
|
||||
|
@ -52,9 +56,19 @@ module TS.SpaceTac.UI {
|
|||
Tools.setHoverClick(this.sprite, () => battleview.cursorOnShip(ship), () => battleview.cursorOffShip(ship), () => battleview.cursorClicked());
|
||||
|
||||
// Set location
|
||||
this.prevx = ship.arena_x;
|
||||
this.prevy = ship.arena_y;
|
||||
this.position.set(ship.arena_x, ship.arena_y);
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.prevx != this.x || this.prevy != this.y) {
|
||||
this.sprite.rotation = Math.atan2(this.y - this.prevy, this.x - this.prevx);
|
||||
}
|
||||
this.prevx = this.x;
|
||||
this.prevy = this.y;
|
||||
}
|
||||
|
||||
// Set the hovered state on this ship
|
||||
// This will toggle the hover effect
|
||||
setHovered(hovered: boolean) {
|
||||
|
@ -67,25 +81,36 @@ module TS.SpaceTac.UI {
|
|||
this.frame.loadTexture(`battle-arena-ship-${playing ? "playing" : "normal"}-${this.enemy ? "enemy" : "own"}`);
|
||||
}
|
||||
|
||||
// Move the sprite to a location
|
||||
moveTo(x: number, y: number, facing_angle: number, animate: boolean = true, on_complete: Function | null = null) {
|
||||
/**
|
||||
* Move the sprite to a location
|
||||
*
|
||||
* Return the duration of animation
|
||||
*/
|
||||
moveTo(x: number, y: number, facing_angle: number, animate = true): number {
|
||||
if (animate) {
|
||||
var tween_group = this.game.tweens.create(this);
|
||||
var tween_sprite = this.game.tweens.create(this.sprite);
|
||||
tween_group.to({ x: x, y: y });
|
||||
tween_group.start();
|
||||
Tools.rotationTween(tween_sprite, facing_angle);
|
||||
if (on_complete) {
|
||||
tween_sprite.onComplete.addOnce(on_complete);
|
||||
if (x == this.x && y == this.y) {
|
||||
let tween = this.game.tweens.create(this.sprite);
|
||||
let duration = Animation.rotationTween(tween, facing_angle, 0.3);
|
||||
tween.start();
|
||||
return duration;
|
||||
} else {
|
||||
let distance = Target.newFromLocation(this.x, this.y).getDistanceTo(Target.newFromLocation(x, y));
|
||||
var tween = this.game.tweens.create(this);
|
||||
let duration = Math.sqrt(distance / 1000) * 3000;
|
||||
let curve_force = distance * 0.4;
|
||||
tween.to({
|
||||
x: [this.x + Math.cos(this.sprite.rotation) * curve_force, x - Math.cos(facing_angle) * curve_force, x],
|
||||
y: [this.y + Math.sin(this.sprite.rotation) * curve_force, y - Math.sin(facing_angle) * curve_force, y]
|
||||
}, duration, Phaser.Easing.Sinusoidal.InOut);
|
||||
tween.interpolation((v, k) => Phaser.Math.bezierInterpolation(v, k));
|
||||
tween.start();
|
||||
return duration;
|
||||
}
|
||||
tween_sprite.start();
|
||||
} else {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.sprite.rotation = facing_angle;
|
||||
if (on_complete) {
|
||||
on_complete();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,13 +161,15 @@ module TS.SpaceTac.UI {
|
|||
this.ship_hovered = ship;
|
||||
this.arena.setShipHovered(ship);
|
||||
this.ship_list.setHovered(ship);
|
||||
this.ship_tooltip.setShip(ship);
|
||||
if (this.targetting) {
|
||||
if (ship) {
|
||||
this.targetting.setTargetShip(ship);
|
||||
} else {
|
||||
this.targetting.unsetTarget();
|
||||
}
|
||||
this.ship_tooltip.setShip(null);
|
||||
} else {
|
||||
this.ship_tooltip.setShip(ship);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,20 +14,51 @@ module TS.SpaceTac.UI {
|
|||
// Subscription identifier
|
||||
private subscription: any;
|
||||
|
||||
// Create a log processor, linked to a battleview
|
||||
// Delay before processing next events
|
||||
private delayed = false;
|
||||
|
||||
// Processing queue, when delay is active
|
||||
private queue: BaseLogEvent[] = [];
|
||||
|
||||
constructor(view: BattleView) {
|
||||
this.view = view;
|
||||
this.battle = view.battle;
|
||||
this.log = view.battle.log;
|
||||
|
||||
this.subscription = this.log.subscribe((event: BaseLogEvent) => {
|
||||
this.processBattleEvent(event);
|
||||
});
|
||||
this.subscription = this.log.subscribe(event => this.processBattleEvent(event));
|
||||
this.battle.injectInitialEvents();
|
||||
}
|
||||
|
||||
// Process a BaseLogEvent
|
||||
/**
|
||||
* Introduce a delay in event processing
|
||||
*/
|
||||
delayNextEvents(duration: number) {
|
||||
if (duration > 0 && !this.view.gameui.headless) {
|
||||
this.delayed = true;
|
||||
setTimeout(() => this.processQueued(), duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the events queued due to a delay
|
||||
*/
|
||||
processQueued() {
|
||||
let events = acopy(this.queue);
|
||||
this.queue = [];
|
||||
this.delayed = false;
|
||||
|
||||
events.forEach(event => this.processBattleEvent(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single event
|
||||
*/
|
||||
processBattleEvent(event: BaseLogEvent) {
|
||||
if (this.delayed) {
|
||||
this.queue.push(event);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Battle event", event);
|
||||
|
||||
if (event instanceof ShipChangeEvent) {
|
||||
|
@ -82,7 +113,8 @@ module TS.SpaceTac.UI {
|
|||
private processMoveEvent(event: MoveEvent): void {
|
||||
var sprite = this.view.arena.findShipSprite(event.ship);
|
||||
if (sprite) {
|
||||
sprite.moveTo(event.target.x, event.target.y, event.facing_angle, true);
|
||||
let duration = sprite.moveTo(event.target.x, event.target.y, event.facing_angle, !event.initial);
|
||||
this.delayNextEvents(duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,13 +145,10 @@ module TS.SpaceTac.UI {
|
|||
var source = Target.newFromShip(event.ship);
|
||||
var destination = event.target;
|
||||
|
||||
// Face the target
|
||||
var attacker = this.view.arena.findShipSprite(event.ship);
|
||||
var angle = source.getAngleTo(destination);
|
||||
attacker.moveTo(source.x, source.y, angle, true);
|
||||
|
||||
var effect = new WeaponEffect(this.view.arena, source, destination, event.weapon.code);
|
||||
effect.start();
|
||||
let duration = effect.start();
|
||||
|
||||
this.delayNextEvents(duration);
|
||||
}
|
||||
|
||||
// Battle ended (victory or defeat)
|
||||
|
@ -146,7 +175,8 @@ module TS.SpaceTac.UI {
|
|||
|
||||
// New drone deployed
|
||||
private processDroneDeployedEvent(event: DroneDeployedEvent): void {
|
||||
this.view.arena.addDrone(event.drone);
|
||||
let duration = this.view.arena.addDrone(event.drone);
|
||||
this.delayNextEvents(duration);
|
||||
}
|
||||
|
||||
// Drone destroyed
|
||||
|
|
|
@ -21,6 +21,7 @@ module TS.SpaceTac.UI.Specs {
|
|||
let effect = new WeaponEffect(battleview.arena, new Target(10, 0), Target.newFromShip(ship), "test");
|
||||
|
||||
let mock_shield_impact = spyOn(effect, "shieldImpactEffect").and.stub();
|
||||
let mock_hull_impact = spyOn(effect, "hullImpactEffect").and.stub();
|
||||
|
||||
effect.gunEffect();
|
||||
|
||||
|
@ -29,11 +30,14 @@ module TS.SpaceTac.UI.Specs {
|
|||
|
||||
expect(layer.children[0] instanceof Phaser.Particles.Arcade.Emitter).toBe(true);
|
||||
expect(mock_shield_impact).toHaveBeenCalledTimes(0);
|
||||
expect(mock_hull_impact).toHaveBeenCalledTimes(1);
|
||||
expect(mock_hull_impact).toHaveBeenCalledWith(jasmine.objectContaining({ x: 10, y: 0 }), jasmine.objectContaining({ x: 50, y: 30 }), 100, 800);
|
||||
|
||||
TestTools.setShipHP(ship, 10, 10);
|
||||
effect.gunEffect();
|
||||
expect(mock_shield_impact).toHaveBeenCalledTimes(1);
|
||||
expect(mock_shield_impact).toHaveBeenCalledWith(jasmine.objectContaining({ x: 10, y: 0 }), jasmine.objectContaining({ x: 50, y: 30 }), 100, 800);
|
||||
expect(mock_hull_impact).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,10 +43,16 @@ module TS.SpaceTac.UI {
|
|||
this.effect = this.getEffectForWeapon(weapon);
|
||||
}
|
||||
|
||||
// Start the visual effect
|
||||
start(): void {
|
||||
/**
|
||||
* Start the visual effect
|
||||
*
|
||||
* Returns the duration of the effect.
|
||||
*/
|
||||
start(): number {
|
||||
if (this.effect) {
|
||||
this.effect.call(this);
|
||||
return this.effect.call(this);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,14 +93,33 @@ module TS.SpaceTac.UI {
|
|||
emitter.setRotation(0, 0);
|
||||
emitter.setXSpeed(-Math.cos(angle) * 20, -Math.cos(angle) * 80);
|
||||
emitter.setYSpeed(-Math.sin(angle) * 20, -Math.sin(angle) * 80);
|
||||
emitter.start(false, 200, 30, duration / 30);
|
||||
emitter.start(false, 200, 30, duration * 0.8 / 30);
|
||||
this.layer.addChild(emitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hull impact effect on a ship
|
||||
*/
|
||||
hullImpactEffect(from: Point, ship: Point, delay: number, duration: number) {
|
||||
let angle = Math.atan2(from.y - ship.y, from.x - ship.x);
|
||||
|
||||
let emitter = this.ui.add.emitter(ship.x + Math.cos(angle) * 10, ship.y + Math.sin(angle) * 10, 30);
|
||||
emitter.minParticleScale = 1.0;
|
||||
emitter.maxParticleScale = 2.0;
|
||||
emitter.gravity = 0;
|
||||
emitter.makeParticles("battle-weapon-hot");
|
||||
emitter.setSize(15, 15);
|
||||
emitter.setRotation(0, 0);
|
||||
emitter.setXSpeed(-Math.cos(angle) * 120, -Math.cos(angle) * 260);
|
||||
emitter.setYSpeed(-Math.sin(angle) * 120, -Math.sin(angle) * 260);
|
||||
emitter.start(false, 200, 30, duration * 0.8 / 30);
|
||||
this.layer.addChild(emitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default firing effect
|
||||
*/
|
||||
defaultEffect(): void {
|
||||
defaultEffect(): number {
|
||||
var missile = new Phaser.Sprite(this.ui, this.source.x, this.source.y, "battle-weapon-default");
|
||||
missile.anchor.set(0.5, 0.5);
|
||||
missile.rotation = this.source.getAngleTo(this.destination);
|
||||
|
@ -106,12 +131,14 @@ module TS.SpaceTac.UI {
|
|||
missile.destroy();
|
||||
});
|
||||
tween.start();
|
||||
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submachine gun effect (quick chain of small bullets)
|
||||
*/
|
||||
gunEffect(): void {
|
||||
gunEffect(): number {
|
||||
this.ui.audio.playOnce("battle-weapon-bullets");
|
||||
|
||||
let has_shield = this.destination.ship && this.destination.ship.getValue("shield") > 0;
|
||||
|
@ -129,12 +156,20 @@ module TS.SpaceTac.UI {
|
|||
emitter.setXSpeed(Math.cos(angle) * speed, Math.cos(angle) * speed);
|
||||
emitter.setYSpeed(Math.sin(angle) * speed, Math.sin(angle) * speed);
|
||||
emitter.makeParticles(["battle-weapon-bullets"]);
|
||||
emitter.start(false, 1000 * (distance - 50 - (has_shield ? 80 : 40)) / speed, 50, 10);
|
||||
let guard = 50 + (has_shield ? 80 : 40);
|
||||
if (guard + 1 > distance) {
|
||||
guard = distance - 1;
|
||||
}
|
||||
emitter.start(false, 1000 * (distance - guard) / speed, 50, 10);
|
||||
this.layer.addChild(emitter);
|
||||
|
||||
if (has_shield) {
|
||||
this.shieldImpactEffect(this.source, this.destination, 100, 800);
|
||||
} else {
|
||||
this.hullImpactEffect(this.source, this.destination, 100, 800);
|
||||
}
|
||||
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/ui/common/Animation.spec.ts
Normal file
15
src/ui/common/Animation.spec.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
module TS.SpaceTac.UI.Specs {
|
||||
describe("Animation", () => {
|
||||
ingame_it("animates rotation", function (game) {
|
||||
let obj = { rotation: -Math.PI * 2.5 };
|
||||
let tween = game.tweens.create(obj);
|
||||
let result = Animation.rotationTween(tween, Math.PI * 0.25, 1, Phaser.Easing.Linear.None);
|
||||
expect(result).toEqual(750);
|
||||
expect(tween.generateData(4)).toEqual([
|
||||
{ rotation: -Math.PI * 0.25 },
|
||||
{ rotation: 0 },
|
||||
{ rotation: Math.PI * 0.25 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -28,5 +28,35 @@ module TS.SpaceTac.UI {
|
|||
Animation.fadeOut(game, obj, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate a rotation value
|
||||
*
|
||||
* This will take into account the 2*pi modulo
|
||||
*
|
||||
* Returns the duration
|
||||
*/
|
||||
static rotationTween(tween: Phaser.Tween, dest: number, speed = 1, easing = Phaser.Easing.Cubic.InOut, property = "rotation"): number {
|
||||
// Immediately change the object's current rotation to be in range (-pi,pi)
|
||||
let value = Tools.normalizeAngle(tween.target[property]);
|
||||
tween.target[property] = value;
|
||||
|
||||
// Compute destination angle
|
||||
dest = Tools.normalizeAngle(dest);
|
||||
if (value - dest > Math.PI) {
|
||||
dest += 2 * Math.PI;
|
||||
} else if (value - dest < -Math.PI) {
|
||||
dest -= 2 * Math.PI;
|
||||
}
|
||||
let distance = Math.abs(Tools.normalizeAngle(dest - value)) / Math.PI;
|
||||
let duration = distance * 1000 / speed;
|
||||
|
||||
// Update the tween
|
||||
let changes = {};
|
||||
changes[property] = dest;
|
||||
tween.to(changes, duration, easing);
|
||||
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,24 +64,5 @@ module TS.SpaceTac.UI {
|
|||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate a rotation value
|
||||
// This will take into account the 2*pi modulo
|
||||
static rotationTween(tween: Phaser.Tween, dest: number, property: string = "rotation"): void {
|
||||
// Immediately change the object's current rotation to be in range (-pi,pi)
|
||||
var value = Tools.normalizeAngle(tween.target[property]);
|
||||
tween.target[property] = value;
|
||||
|
||||
// Update the tween
|
||||
dest = Tools.normalizeAngle(dest);
|
||||
if (value - dest > Math.PI) {
|
||||
dest += 2 * Math.PI;
|
||||
} else if (value - dest < -Math.PI) {
|
||||
dest -= 2 * Math.PI;
|
||||
}
|
||||
var changes: Object = {};
|
||||
changes[property] = dest;
|
||||
tween.to(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue