mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
Merge pull request #57 from zbates26/main
Shield/Hold Animation + Bounce Movement Animation + Unique Arrows for movements + Quality of life changes
This commit is contained in:
commit
4e13f246d7
4 changed files with 204 additions and 15 deletions
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
|
|
@ -4,11 +4,11 @@
|
||||||
{
|
{
|
||||||
"type": "pwa-chrome",
|
"type": "pwa-chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Chrome Debug 5179",
|
"name": "Chrome Debug 5173",
|
||||||
"url": "http://localhost:5179",
|
"url": "http://localhost:5173",
|
||||||
"webRoot": "${workspaceFolder}/ai_animation/",
|
"webRoot": "${workspaceFolder}/ai_animation/",
|
||||||
"sourceMapPathOverrides": {
|
"sourceMapPathOverrides": {
|
||||||
"http://localhost:5179/*": "${webRoot}/*"
|
"http://localhost:5173/*": "${webRoot}/*"
|
||||||
},
|
},
|
||||||
"runtimeArgs": [
|
"runtimeArgs": [
|
||||||
"--remote-debugging-port=9223"
|
"--remote-debugging-port=9223"
|
||||||
|
|
|
||||||
14
.vscode/tasks.json
vendored
Normal file
14
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Run Game",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm start",
|
||||||
|
"presentation": {
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -61,12 +61,7 @@ export class EventQueue {
|
||||||
* Remove resolved events from the queue
|
* Remove resolved events from the queue
|
||||||
*/
|
*/
|
||||||
cleanup(): void {
|
cleanup(): void {
|
||||||
let clearedQueue = this.events.filter(event => !event.resolved);
|
this.events = this.events.filter(e => !e.resolved);
|
||||||
if (clearedQueue.length <= 1) {
|
|
||||||
console.log(this.events)
|
|
||||||
throw new Error("We've cleared all the messages out of the queue")
|
|
||||||
}
|
|
||||||
this.events = clearedQueue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,66 @@ import { config } from "../config"; // Assuming config is defined in a separate
|
||||||
import { PowerENUM, ProvinceENUM } from "../types/map";
|
import { PowerENUM, ProvinceENUM } from "../types/map";
|
||||||
import { UnitTypeENUM } from "../types/units";
|
import { UnitTypeENUM } from "../types/units";
|
||||||
import { sineWave, getTimeInSeconds } from "../utils/timing";
|
import { sineWave, getTimeInSeconds } from "../utils/timing";
|
||||||
|
//import { color } from "chart.js/helpers";
|
||||||
|
//import { depth } from "three/tsl";
|
||||||
|
|
||||||
|
|
||||||
|
function buildFancyArrow(length: number, colorDeter: string): THREE.Mesh {
|
||||||
|
//Specs to change arrow size
|
||||||
|
const tw = 3.5;
|
||||||
|
const headLength = tw * 4;
|
||||||
|
const hw = tw * 4;
|
||||||
|
|
||||||
|
const shape = new THREE.Shape()
|
||||||
|
.moveTo(0,tw/2)
|
||||||
|
.lineTo(length- headLength,tw/2)
|
||||||
|
.lineTo(length-headLength,hw/2)
|
||||||
|
.lineTo(length,0)
|
||||||
|
.lineTo(length-headLength,-hw/2)
|
||||||
|
.lineTo(length-headLength,-tw/2)
|
||||||
|
.lineTo(0,-tw/2)
|
||||||
|
.closePath();
|
||||||
|
|
||||||
|
const extrude= new THREE.ExtrudeGeometry(shape, {depth: 1})
|
||||||
|
let mat: THREE.Material;
|
||||||
|
|
||||||
|
if (colorDeter=='Move') {
|
||||||
|
mat = new THREE.MeshStandardMaterial({color: 0x00FF00})
|
||||||
|
} else if (colorDeter=='Bounce') {
|
||||||
|
mat = new THREE.MeshStandardMaterial({color: 0xFF0000})
|
||||||
|
}
|
||||||
|
const mesh= new THREE.Mesh(extrude, mat)
|
||||||
|
//(Potential Addition: Outline of arrow)
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createShield() {
|
||||||
|
|
||||||
|
//Shield Specs
|
||||||
|
const BSH= 10
|
||||||
|
const SW= 10
|
||||||
|
const TSH= 10
|
||||||
|
|
||||||
|
|
||||||
|
const outL= new THREE.Shape()
|
||||||
|
.moveTo(0, TSH)
|
||||||
|
.lineTo(SW, TSH)
|
||||||
|
.lineTo(SW, 0)
|
||||||
|
.quadraticCurveTo(SW, -BSH, 0, -BSH)
|
||||||
|
.quadraticCurveTo(-SW, -BSH, -SW, 0)
|
||||||
|
.lineTo(-SW,TSH)
|
||||||
|
.lineTo(0, TSH)
|
||||||
|
.closePath();
|
||||||
|
|
||||||
|
const SExtrude= new THREE.ExtrudeGeometry(outL, {depth: 5})
|
||||||
|
|
||||||
|
const SMat= new THREE.MeshStandardMaterial({color: 0x00FF00})
|
||||||
|
|
||||||
|
const SMesh= new THREE.Mesh(SExtrude, SMat )
|
||||||
|
|
||||||
|
return SMesh
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function getUnit(unitOrder: UnitOrder, power: string) {
|
function getUnit(unitOrder: UnitOrder, power: string) {
|
||||||
if (power === undefined) throw new Error("Must pass the power argument, cannot be undefined")
|
if (power === undefined) throw new Error("Must pass the power argument, cannot be undefined")
|
||||||
|
|
@ -54,7 +114,34 @@ function createMoveAnimation(unitMesh: THREE.Group, orderDestination: ProvinceEN
|
||||||
// Store animation start time for consistent wave motion
|
// Store animation start time for consistent wave motion
|
||||||
const animStartTime = getTimeInSeconds();
|
const animStartTime = getTimeInSeconds();
|
||||||
|
|
||||||
let anim = new Tween(unitMesh.position)
|
const start = new THREE.Vector3();
|
||||||
|
//prevents the arrow mesh from using local coords which mess with the alignment
|
||||||
|
unitMesh.getWorldPosition(start);
|
||||||
|
const end = getProvincePosition(orderDestination)!;
|
||||||
|
//lines to determine direction and length of the arrow, minus a little from the length so it's offset
|
||||||
|
const direct= new THREE.Vector3().subVectors(end, start);
|
||||||
|
const length= Math.max(direct.length() - 2, 0);
|
||||||
|
const arrowMesh= buildFancyArrow(length,'Move');
|
||||||
|
const dir = direct.clone().normalize();
|
||||||
|
|
||||||
|
arrowMesh.position.copy(start);
|
||||||
|
|
||||||
|
//Core of the arrow alignment, won't work without this
|
||||||
|
const q = new THREE.Quaternion().setFromUnitVectors(
|
||||||
|
new THREE.Vector3(1, 0, 0),dir
|
||||||
|
);
|
||||||
|
arrowMesh.setRotationFromQuaternion(q);
|
||||||
|
|
||||||
|
arrowMesh.scale.set(0, 1, 1);
|
||||||
|
|
||||||
|
gameState.scene.add(arrowMesh);
|
||||||
|
unitMesh.userData.moveArrow = arrowMesh;
|
||||||
|
//Value beside x:1 controls the speed of the growth
|
||||||
|
const ArrowGrow = new Tween(arrowMesh.scale)
|
||||||
|
.to({ x: 1 }, 1000)
|
||||||
|
.easing(Easing.Quadratic.Out)
|
||||||
|
.onComplete(() => {
|
||||||
|
const anim = new Tween(unitMesh.position)
|
||||||
.to({
|
.to({
|
||||||
x: destinationVector.x,
|
x: destinationVector.x,
|
||||||
y: 10,
|
y: 10,
|
||||||
|
|
@ -76,20 +163,109 @@ function createMoveAnimation(unitMesh: THREE.Group, orderDestination: ProvinceEN
|
||||||
unitMesh.rotation.z = 0;
|
unitMesh.rotation.z = 0;
|
||||||
unitMesh.rotation.x = 0;
|
unitMesh.rotation.x = 0;
|
||||||
}
|
}
|
||||||
|
gameState.scene.remove(arrowMesh);
|
||||||
|
delete unitMesh.userData.moveArrow;
|
||||||
unitMesh.userData.isAnimating = false
|
unitMesh.userData.isAnimating = false
|
||||||
})
|
})
|
||||||
.start();
|
.start();
|
||||||
gameState.unitAnimations.push(anim);
|
gameState.unitAnimations.push(anim);})
|
||||||
return anim
|
.start()
|
||||||
|
gameState.unitAnimations.push(ArrowGrow)
|
||||||
|
return ArrowGrow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Animation for bounce
|
||||||
|
|
||||||
|
function createBounceAnimation(unitMesh: THREE.Group, attemptedDestination: ProvinceENUM): Tween {
|
||||||
|
const end = getProvincePosition(attemptedDestination)!;
|
||||||
|
if (!end) throw new Error(`No position found for attempted destination: ${attemptedDestination}`);
|
||||||
|
|
||||||
|
unitMesh.userData.isAnimating = true;
|
||||||
|
|
||||||
|
|
||||||
|
const start = new THREE.Vector3();
|
||||||
|
//prevents the arrow mesh from using local coords which mess with the alignment
|
||||||
|
unitMesh.getWorldPosition(start);
|
||||||
|
//lines to determine direction and length of the arrow, minus a little from the length so it's offset
|
||||||
|
const direct= new THREE.Vector3().subVectors(end, start);
|
||||||
|
const length= Math.max(direct.length() - 2, 0);
|
||||||
|
const arrowMesh= buildFancyArrow(length,'Bounce');
|
||||||
|
const dir = direct.clone().normalize();
|
||||||
|
|
||||||
|
arrowMesh.position.copy(start);
|
||||||
|
|
||||||
|
//Core of the arrow alignment, won't work without this
|
||||||
|
const q = new THREE.Quaternion().setFromUnitVectors(
|
||||||
|
new THREE.Vector3(1, 0, 0), dir
|
||||||
|
);
|
||||||
|
arrowMesh.setRotationFromQuaternion(q);
|
||||||
|
|
||||||
|
arrowMesh.scale.set(0, 1, 1);
|
||||||
|
|
||||||
|
gameState.scene.add(arrowMesh);
|
||||||
|
unitMesh.userData.moveArrow = arrowMesh;
|
||||||
|
const growBounce= new Tween (arrowMesh.scale)
|
||||||
|
//Number beside x:1 controls speed in ms
|
||||||
|
.to({ x: 1 }, 1000)
|
||||||
|
.easing(Easing.Quadratic.Out)
|
||||||
|
.onComplete(() => {
|
||||||
|
const bounceOut = new Tween(unitMesh.position)
|
||||||
|
.to({ x: end.x, y: 10, z: end.z }, config.effectiveAnimationDuration / 2)
|
||||||
|
.easing(Easing.Quadratic.Out)
|
||||||
|
.repeat(1)
|
||||||
|
.yoyo(true)
|
||||||
|
.onComplete(() => {
|
||||||
|
if (unitMesh.userData.moveArrow) {
|
||||||
|
gameState.scene.remove(arrowMesh);
|
||||||
|
delete unitMesh.userData.moveArrow;
|
||||||
|
unitMesh.userData.isAnimating = false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bounceOut.start();
|
||||||
|
|
||||||
|
gameState.unitAnimations.push(bounceOut);})
|
||||||
|
growBounce.start()
|
||||||
|
gameState.unitAnimations.push(growBounce)
|
||||||
|
return growBounce;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createHoldAnimation(unitMesh: THREE.Group): Tween {
|
||||||
|
// 1) Build the shield mesh
|
||||||
|
const shield = createShield();
|
||||||
|
|
||||||
|
// 2) Figure out where the unit’s feet are
|
||||||
|
const worldPos = new THREE.Vector3();
|
||||||
|
unitMesh.getWorldPosition(worldPos);
|
||||||
|
|
||||||
|
shield.position.set(worldPos.x, 16, worldPos.z+8);
|
||||||
|
shield.scale.set(1, 0, 1); // collapse height
|
||||||
|
|
||||||
|
gameState.scene.add(shield);
|
||||||
|
unitMesh.userData.newshield = shield;
|
||||||
|
unitMesh.userData.isAnimating = true;
|
||||||
|
|
||||||
|
const growTween = new Tween(shield.scale)
|
||||||
|
.to({ x: 1, y: 1, z: 1 }, 2000)
|
||||||
|
.easing(Easing.Quadratic.Out)
|
||||||
|
.onComplete(() => {
|
||||||
|
gameState.scene.remove(shield);
|
||||||
|
unitMesh.userData.isAnimating = false;
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
|
||||||
|
gameState.unitAnimations.push(growTween);
|
||||||
|
|
||||||
|
return growTween;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Creates animations for unit movements based on orders from the previous phase
|
* Creates animations for unit movements based on orders from the previous phase
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
export function createAnimationsForNextPhase() {
|
export function createAnimationsForNextPhase() {
|
||||||
let previousPhase = gameState.gameData?.phases[gameState.phaseIndex == 0 ? 0 : gameState.phaseIndex - 1]
|
let previousPhase = gameState.gameData?.phases[gameState.phaseIndex == 0 ? 0 : gameState.phaseIndex - 1]
|
||||||
|
// const sequence = ["build", "disband", "hold", "move", "bounce", "retreat"]
|
||||||
// Safety check - if no previous phase or no orders, return
|
// Safety check - if no previous phase or no orders, return
|
||||||
if (!previousPhase) {
|
if (!previousPhase) {
|
||||||
logger.log("No previous phase to animate");
|
logger.log("No previous phase to animate");
|
||||||
|
|
@ -152,10 +328,14 @@ export function createAnimationsForNextPhase() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "bounce":
|
case "bounce":
|
||||||
// TODO: implement bounce animation
|
if (unitIndex < 0) throw new Error("Unable to find unit for order " + order.raw)
|
||||||
|
if (!order.destination) throw new Error("Bounce order without destination")
|
||||||
|
createBounceAnimation(gameState.unitMeshes[unitIndex], order.destination as ProvinceENUM);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "hold":
|
case "hold":
|
||||||
//TODO: Hold animation, maybe a sheild or something?
|
//TODO: Hold animation, maybe a sheild or something?
|
||||||
|
createHoldAnimation(gameState.unitMeshes[unitIndex])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "convoy":
|
case "convoy":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue