squidGame/tgx-games-client/assets/level1/Script/WoodenManGame.ts

318 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { _decorator, Component, Node, Prefab, Label, Animation, instantiate, Vec3, tween } from 'cc';
import { WoodenManPlayer } from './WoodenManPlayer';
import { ModuleDef } from '../../scripts/ModuleDef';
import { resLoader } from '../../core_tgx/base/utils/ResLoader';
const { ccclass, property } = _decorator;
// 玩家状态
enum PlayerState {
ALIVE, // 存活
DEAD, // 死亡
QUALIFIED // 已晋级
}
// 游戏阶段
export enum GamePhase {
CHOOSE, // 选择阶段
MOVING, // 前进阶段
ROUND_END, // 回合结束
GAME_OVER // 游戏结束
}
// 玩家数据
interface PlayerData {
id: number;
position: number; // 当前位置
state: PlayerState;
selectedSteps: number; // 选择的步数
}
@ccclass('WoodenManGame')
export class WoodenManGame extends Component {
@property(Prefab)
rolePrefab: Prefab = null; // 角色预制体
@property(Node)
dollNode: Node = null; // 木头人节点
@property(Label)
aliveCountLabel: Label = null; // 存活人数显示
@property(Node)
playersNode: Node = null; // 存放所有玩家的节点
// 游戏配置
private readonly TOTAL_ROUNDS = 15; // 总回合数
private readonly ROUND_TIME = 20; // 每回合时间(秒)
private readonly TOTAL_DISTANCE = 12; // 终点距离(米)
private readonly MIN_QUALIFIED = 20; // 最少晋级人数
private readonly INITIAL_PLAYERS = 36; // 初始玩家数
private readonly STEP_OPTIONS = [1, 2, 3, 4, 5, 6]; // 可选步数
// 游戏状态
private currentRound = 0;
private currentPhase = GamePhase.CHOOSE;
private remainingTime = 0;
private dollTurnTime = 0; // 娃娃回头时间
private players: Map<number, PlayerData> = new Map();
private aiPlayers: Node[] = []; // AI玩家节点列表
private playerNodes: Map<number, Node> = new Map(); // 所有玩家节点映射
async start() {
await this.initPlayers();
this.initGame();
}
private async initPlayers() {
// 创建本地玩家
const localPlayer = await this.createPlayer(0, true);
this.playerNodes.set(0, localPlayer);
// 创建AI玩家
for (let i = 1; i < this.INITIAL_PLAYERS; i++) {
const aiPlayer = await this.createPlayer(i, false);
this.playerNodes.set(i, aiPlayer);
this.aiPlayers.push(aiPlayer);
}
this.updateAliveCount();
}
private async createPlayer(id: number, isLocal: boolean) {
// 随机位置:以起点为中心,随机分布
const x = Math.random() * 10 - 5; // -5到5
const z = isLocal ? 0 : Math.random() * 4 - 2; // 本地玩家在起点AI随机分布
// 随机角色ID1-5
const roleId = Math.floor(Math.random() * 5) + 1;
const roleIdStr = ('000' + roleId).slice(-3);
const prefab: Prefab = await new Promise<Prefab>((resolve) => {
resLoader.get(`common/role/Character${roleIdStr}`, Prefab, ModuleDef.Arean);
});
if (prefab) {
const role = instantiate(prefab);
this.playersNode.addChild(role);
role.setPosition(new Vec3(x, 0, z));
role.setRotationFromEuler(0, 180, 0);
// 添加玩家组件
const playerComp = role.addComponent(WoodenManPlayer);
playerComp.init(id, isLocal);
return role;
}
return null;
}
// 更新存活人数显示
private updateAliveCount() {
const aliveCount = Array.from(this.players.values())
.filter(p => p.state === PlayerState.ALIVE).length;
this.aliveCountLabel.string = `存活: ${aliveCount}/${this.INITIAL_PLAYERS}`;
}
private initGame() {
// 初始化玩家
for (let i = 0; i < this.INITIAL_PLAYERS; i++) {
this.players.set(i, {
id: i,
position: 0,
state: PlayerState.ALIVE,
selectedSteps: 0
});
}
this.startNewRound();
}
private startNewRound() {
this.currentRound++;
this.remainingTime = this.ROUND_TIME;
this.currentPhase = GamePhase.CHOOSE;
this.dollTurnTime = Math.floor(Math.random() * 6) + 1; // 1-6秒随机
// 重置所有存活玩家的选择
this.players.forEach(player => {
if (player.state === PlayerState.ALIVE) {
player.selectedSteps = 0;
}
});
}
// 玩家选择步数
public playerChooseSteps(playerId: number, steps: number) {
if (this.currentPhase !== GamePhase.CHOOSE) return;
const player = this.players.get(playerId);
if (!player || player.state !== PlayerState.ALIVE) return;
if (this.STEP_OPTIONS.indexOf(steps) !== -1) {
player.selectedSteps = steps;
}
}
// 执行移动阶段
private executeMovingPhase() {
this.currentPhase = GamePhase.MOVING;
// 播放木头人转身动画
this.playDollTurnAnimation(() => {
this.players.forEach((player, id) => {
if (player.state !== PlayerState.ALIVE) return;
const steps = player.selectedSteps || 1;
const playerNode = this.playerNodes.get(id);
if (steps > this.dollTurnTime) {
// 被抓到,播放死亡动画
this.killPlayer(player, playerNode);
} else {
// 移动玩家
this.movePlayer(player, playerNode, steps);
}
});
this.updateAliveCount();
});
}
// 木头人转身动画
private playDollTurnAnimation(callback: () => void) {
const duration = 1.0; // 转身动画持续时间
const currentRotation = this.dollNode.eulerAngles.clone();
const targetRotation = new Vec3(currentRotation.x, currentRotation.y + 180, currentRotation.z);
// 使用 tween 实现平滑旋转
tween(this.dollNode)
.to(duration, { eulerAngles: targetRotation })
.call(callback)
.start();
}
// 玩家死亡效果
private killPlayer(player: PlayerData, playerNode: Node) {
player.state = PlayerState.DEAD;
// 获取玩家组件
const playerComp = playerNode.getComponent(WoodenManPlayer);
if (playerComp) {
playerComp.setAlive(false);
// 播放死亡动画,动画结束后重置位置
playerComp.playDieAnimation(() => {
player.position = 0;
// 重置到起点
playerNode.setPosition(new Vec3(
Math.random() * 10 - 5,
0,
0
));
});
}
}
// 玩家移动效果
private movePlayer(player: PlayerData, playerNode: Node, steps: number) {
const targetPos = new Vec3(
playerNode.position.x,
0,
player.position + steps
);
// 使用 tween 实现平滑移动
tween(playerNode)
.to(steps, { position: targetPos })
.start();
player.position += steps;
if (player.position >= this.TOTAL_DISTANCE) {
player.state = PlayerState.QUALIFIED;
player.position = this.TOTAL_DISTANCE;
// 播放胜利动画
const anim = playerNode.getComponent(Animation);
anim.play('victory');
}
}
// AI决策
private updateAIDecisions() {
if (this.currentPhase !== GamePhase.CHOOSE) return;
this.aiPlayers.forEach(aiNode => {
const playerComp = aiNode.getComponent(WoodenManPlayer);
if (!playerComp || !playerComp.isAlive()) return;
// 简单AI逻辑随机选择1-3步
const steps = Math.floor(Math.random() * 3) + 1;
this.playerChooseSteps(playerComp.getId(), steps);
});
}
// 检查游戏是否结束
private checkGameEnd(): boolean {
let qualifiedCount = 0;
this.players.forEach(player => {
if (player.state === PlayerState.QUALIFIED) {
qualifiedCount++;
}
});
return qualifiedCount >= this.MIN_QUALIFIED || this.currentRound >= this.TOTAL_ROUNDS;
}
// 游戏主循环
update(dt: number) {
if (this.currentPhase === GamePhase.GAME_OVER) return;
this.remainingTime -= dt;
if (this.currentPhase === GamePhase.CHOOSE && this.remainingTime <= 0) {
this.executeMovingPhase();
}
if (this.currentPhase === GamePhase.MOVING) {
// 移动阶段结束后
this.currentPhase = GamePhase.ROUND_END;
if (this.checkGameEnd()) {
this.endGame();
} else {
this.startNewRound();
}
}
// 更新AI决策
this.updateAIDecisions();
}
private endGame() {
this.currentPhase = GamePhase.GAME_OVER;
// 淘汰未晋级玩家
this.players.forEach(player => {
if (player.state !== PlayerState.QUALIFIED) {
player.state = PlayerState.DEAD;
}
});
// 这里可以触发游戏结束事件
this.node.emit('gameOver');
}
// 获取游戏状态
public getGameState() {
return {
currentRound: this.currentRound,
currentPhase: this.currentPhase,
remainingTime: this.remainingTime,
players: Array.from(this.players.values())
};
}
public getCurrentPhase(): GamePhase {
return this.currentPhase;
}
}