Files

235 lines
6.5 KiB
JavaScript
Raw Permalink 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.
// objects/Unit.js
export default class Unit extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y, texture) {
super(scene, x, y, texture);
scene.add.existing(this);
scene.physics.add.existing(this);
// 设置角色原点为底部中点
this.setOrigin(0.5, 1);
// 设置角色深度,确保显示在指示器上方
this.setDepth(10);
// 启用圆形碰撞体(更自然)
this.setCircle(14);
// 启用与世界边界的碰撞(必须配合 physics.world.setBounds)
this.setCollideWorldBounds(true);
// 移动目标(x, y)
this.target = null;
// 选中状态
this.isSelected = false;
this.selectedIndicator = null;
this.createSelectedIndicator();
// 目标地点显示
this.targetIndicator = null;
this.pathGraphics = null;
// 移动控制
this.canMove = true;
}
/**
* 创建选中状态指示器
*/
createSelectedIndicator() {
// 创建一个外圈指示器,默认隐藏
// 椭圆指示器:中心位于图片底边中心,宽度为角色宽度的1.4倍,高度为角色高度的0.3倍
this.selectedIndicator = this.scene.add.ellipse(this.x, this.y, this.width * 1.5, this.height * 0.5, 0x00ff00, 0.0);
this.selectedIndicator.setStrokeStyle(2, 0x00ff00, 1);
this.selectedIndicator.setVisible(false);
// 确保指示器与单位一起移动
this.selectedIndicator.setOrigin(0.5, 0.5);
// 移除固定位置设置,让指示器随单位和相机一起移动
this.selectedIndicator.setScrollFactor(1, 1);
// 设置较低的深度,确保角色显示在上方
this.selectedIndicator.setDepth(5);
}
/**
* 创建目标地点指示器
*/
createTargetIndicator() {
if (!this.targetIndicator) {
// 创建目标地点指示器(红色圆点)
this.targetIndicator = this.scene.add.circle(0, 0, 8, 0xff0000, 1);
this.targetIndicator.setOrigin(0.5, 0.5);
this.targetIndicator.setScrollFactor(1, 1);
this.targetIndicator.setVisible(false);
// 设置较低的深度,确保角色显示在上方
this.targetIndicator.setDepth(5);
}
}
/**
* 创建寻路路径图形
*/
createPathGraphics() {
if (!this.pathGraphics) {
// 创建图形对象用于绘制路径
this.pathGraphics = this.scene.add.graphics();
this.pathGraphics.setScrollFactor(1, 1);
// 设置较低的深度,确保角色显示在上方
this.pathGraphics.setDepth(5);
}
}
/**
* 设置单位选中状态
* @param {boolean} isSelected
*/
setSelected(isSelected) {
this.isSelected = isSelected;
if (this.selectedIndicator) {
this.selectedIndicator.setVisible(isSelected);
}
// 只有选中时才显示目标地点和路径
this.updateTargetVisibility();
}
/**
* 更新目标地点和路径的可见性
*/
updateTargetVisibility() {
if (this.targetIndicator) {
this.targetIndicator.setVisible(this.isSelected && this.target !== null);
}
if (this.pathGraphics) {
this.pathGraphics.clear();
// 只有选中且有目标时才绘制路径
if (this.isSelected && this.target !== null) {
this.drawPath();
}
}
}
/**
* 绘制寻路路径
*/
drawPath() {
if (!this.pathGraphics || !this.target) {
return;
}
this.pathGraphics.clear();
this.pathGraphics.lineStyle(2, 0x00ff00, 0.8);
this.pathGraphics.beginPath();
this.pathGraphics.moveTo(this.x, this.y);
this.pathGraphics.lineTo(this.target.x, this.target.y);
this.pathGraphics.stroke();
}
/**
* 设置移动目标点
* @param {number} x
* @param {number} y
*/
setTarget(x, y) {
// 获取世界边界
const worldBounds = this.scene.physics.world.bounds;
// 限制目标点在世界边界内
const clampedX = Math.max(worldBounds.x, Math.min(worldBounds.right, x));
const clampedY = Math.max(worldBounds.y, Math.min(worldBounds.bottom, y));
this.target = { x: clampedX, y: clampedY };
// 创建目标指示器和路径图形(如果不存在)
this.createTargetIndicator();
this.createPathGraphics();
// 更新目标指示器位置
if (this.targetIndicator) {
this.targetIndicator.setPosition(this.target.x, this.target.y);
}
// 更新目标可见性和路径绘制
this.updateTargetVisibility();
}
/**
* 清除目标并停止移动
*/
clearTarget() {
this.target = null;
this.setVelocity(0, 0);
// 隐藏目标指示器和清除路径
if (this.targetIndicator) {
this.targetIndicator.setVisible(false);
}
if (this.pathGraphics) {
this.pathGraphics.clear();
}
}
/**
* 相对移动控制(用于键盘/虚拟摇杆)
* @param {number} x - 水平方向 (-1 到 1)
* @param {number} y - 垂直方向 (-1 到 1)
*/
moveRelative(x, y) {
// 检查是否可以移动
if (this.canMove === false) {
this.setVelocity(0, 0);
return;
}
const speed = 150; // 单位:像素/秒
// 归一化方向向量
const magnitude = Math.sqrt(x * x + y * y);
if (magnitude > 0) {
// 有按键输入时,清除目标点,切换到直接控制模式
this.clearTarget();
const normalizedX = x / magnitude;
const normalizedY = y / magnitude;
this.setVelocity(normalizedX * speed, normalizedY * speed);
} else {
// 没有按键输入时,保持当前的目标点(如果有的话)
this.setVelocity(0, 0);
}
}
/**
* 每帧更新移动逻辑(需在 GameScene.update 中调用)
*/
update() {
// 更新选中指示器位置
if (this.selectedIndicator && this.isSelected) {
// 保持与创建时相同的位置偏移,确保指示器显示在角色底部
this.selectedIndicator.setPosition(this.x, this.y);
}
// 更新路径绘制(单位位置变化时)
if (this.isSelected && this.target !== null) {
this.drawPath();
}
}
updateMovement() {
if (!this.target) {
return;
}
const dx = this.target.x - this.x;
const dy = this.target.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 到达阈值:5 像素内视为“到达”
if (distance < 5) {
// 精确对齐到目标点(可选)
this.setPosition(this.target.x, this.target.y);
this.clearTarget();
} else {
const speed = 150; // 单位:像素/秒
const angle = Math.atan2(dy, dx);
this.setVelocity(Math.cos(angle) * speed, Math.sin(angle) * speed);
}
}
}