2025-09-25 14:57:01 +08:00
|
|
|
|
package uno.mloluyu.characters;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
import com.badlogic.gdx.Gdx;
|
|
|
|
|
|
import com.badlogic.gdx.Input;
|
|
|
|
|
|
import com.badlogic.gdx.graphics.Color;
|
|
|
|
|
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
|
|
|
|
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
|
|
|
|
|
import com.badlogic.gdx.math.Rectangle;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 简化版角色类,仅包含移动、攻击、受击等基础功能。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class SimpleFighter {
|
|
|
|
|
|
|
|
|
|
|
|
private String name; // 角色名称
|
|
|
|
|
|
private final Map<Integer, Float> keyPressDuration = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
private Action currentAction = Action.IDLE; // 当前动作状态(待机、攻击、受击等)
|
|
|
|
|
|
private float verticalSpeed = 0f; // 垂直速度(可用于跳跃或下落)
|
|
|
|
|
|
private boolean isGrounded = true; // 是否在地面上
|
|
|
|
|
|
|
|
|
|
|
|
private Rectangle hitbox = new Rectangle(0, 0, 64, 128); // 碰撞盒,用于位置和受击判定
|
|
|
|
|
|
private Rectangle attackbox = new Rectangle(0, 0, 80, 80); // 攻击盒,用于攻击判定
|
|
|
|
|
|
private boolean isFacingRight = true; // 是否面向右侧
|
|
|
|
|
|
|
|
|
|
|
|
private float speed = 300f; // 移动速度(像素/秒)
|
|
|
|
|
|
private int health = 100; // 当前生命值
|
|
|
|
|
|
private int attackPower = 10; // 攻击力(暂未使用)
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isAttacking = false; // 是否正在攻击(攻击状态标志)
|
|
|
|
|
|
|
|
|
|
|
|
private Iterable<Integer> pressedKeys = new HashMap<Integer, Float>().keySet(); // 初始化 pressedKeys
|
|
|
|
|
|
|
|
|
|
|
|
public SimpleFighter(String name) {
|
|
|
|
|
|
this.name = name; // 构造函数,初始化角色名称
|
2025-09-25 16:50:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void processPressedKeys(float deltaTime) {
|
|
|
|
|
|
for (int keycode : pressedKeys) {
|
|
|
|
|
|
float currentDuration = keyPressDuration.getOrDefault(keycode, 0f);
|
|
|
|
|
|
currentDuration += deltaTime;
|
|
|
|
|
|
keyPressDuration.put(keycode, currentDuration);
|
|
|
|
|
|
handleInput(keycode, true, currentDuration);
|
|
|
|
|
|
}
|
2025-09-25 14:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void update(float deltaTime) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
updateAttackbox("light"); // 默认使用普通攻击的攻击盒更新
|
2025-09-25 14:57:01 +08:00
|
|
|
|
|
|
|
|
|
|
// 攻击只持续一帧
|
|
|
|
|
|
if (isAttacking) {
|
|
|
|
|
|
isAttacking = false;
|
|
|
|
|
|
changeAction(Action.IDLE);
|
|
|
|
|
|
}
|
2025-09-25 16:50:43 +08:00
|
|
|
|
|
|
|
|
|
|
processPressedKeys(deltaTime);
|
|
|
|
|
|
|
2025-09-25 14:57:01 +08:00
|
|
|
|
// 垂直移动(跳跃或重力)
|
|
|
|
|
|
if (!isGrounded) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
verticalSpeed -= 2500 * deltaTime; // 简单重力模拟
|
2025-09-25 14:57:01 +08:00
|
|
|
|
hitbox.y += verticalSpeed * deltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
if (hitbox.y <= 0) {
|
|
|
|
|
|
hitbox.y = 0;
|
|
|
|
|
|
verticalSpeed = 0;
|
|
|
|
|
|
isGrounded = true;
|
|
|
|
|
|
changeAction(Action.IDLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void render(SpriteBatch batch, ShapeRenderer shapeRenderer) {
|
|
|
|
|
|
batch.end(); // 暂停 SpriteBatch 渲染,切换到 ShapeRenderer
|
|
|
|
|
|
System.out.println("人物状态" + currentAction);
|
|
|
|
|
|
boolean isAttacking = currentAction == Action.ATTACK;
|
|
|
|
|
|
shapeRenderer.begin(ShapeRenderer.ShapeType.Line); // 开始绘制线框
|
|
|
|
|
|
|
|
|
|
|
|
shapeRenderer.setColor(Color.BLUE); // 设置颜色为蓝色
|
|
|
|
|
|
shapeRenderer.rect(hitbox.x, hitbox.y, hitbox.width, hitbox.height); // 绘制碰撞盒
|
|
|
|
|
|
|
|
|
|
|
|
if (isAttacking) {
|
|
|
|
|
|
shapeRenderer.setColor(Color.RED); // 设置颜色为红色
|
2025-09-25 16:50:43 +08:00
|
|
|
|
shapeRenderer.rect(attackbox.x, attackbox.y, attackbox.width, attackbox.height); // 绘制攻击框
|
2025-09-25 14:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
shapeRenderer.end(); // 结束 ShapeRenderer 渲染
|
|
|
|
|
|
batch.begin(); // 恢复 SpriteBatch 渲染
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void handleInput(int keycode, boolean isPressed, float duration) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
|
2025-09-25 14:57:01 +08:00
|
|
|
|
if (isPressed) {
|
|
|
|
|
|
if (keycode == Input.Keys.LEFT || keycode == Input.Keys.A) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
move(-1, Gdx.graphics.getDeltaTime());
|
2025-09-25 14:57:01 +08:00
|
|
|
|
} else if (keycode == Input.Keys.RIGHT || keycode == Input.Keys.D) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
move(1, Gdx.graphics.getDeltaTime());
|
2025-09-25 14:57:01 +08:00
|
|
|
|
}
|
2025-09-25 16:50:43 +08:00
|
|
|
|
if (isPressed && !isAttacking) {
|
|
|
|
|
|
if (keycode == Input.Keys.Z || keycode == Input.Keys.J) {
|
|
|
|
|
|
attack("light");
|
|
|
|
|
|
} else if (keycode == Input.Keys.X || keycode == Input.Keys.K) {
|
|
|
|
|
|
attack("heavy");
|
|
|
|
|
|
} else if (keycode == Input.Keys.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT) {
|
|
|
|
|
|
attack("special");
|
|
|
|
|
|
} else if (keycode == Input.Keys.SPACE || keycode == Input.Keys.UP || keycode == Input.Keys.W) {
|
|
|
|
|
|
jump();
|
|
|
|
|
|
}
|
2025-09-25 14:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
keyPressDuration.remove(keycode);
|
|
|
|
|
|
if ((keycode == Input.Keys.LEFT || keycode == Input.Keys.RIGHT || keycode == Input.Keys.A
|
|
|
|
|
|
|| keycode == Input.Keys.D) &&
|
|
|
|
|
|
getCurrentAction() == Action.MOVE) {
|
2025-09-25 14:57:01 +08:00
|
|
|
|
changeAction(Action.IDLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void handleInput(int keycode, boolean isPressed) {
|
|
|
|
|
|
handleInput(keycode, isPressed, 0f); // 调用已有方法,补充默认持续时间
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Action getCurrentAction() {
|
|
|
|
|
|
return currentAction; // 获取当前动作状态
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void changeAction(Action newAction) {
|
|
|
|
|
|
this.currentAction = newAction; // 切换角色动作状态
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void jump() {
|
|
|
|
|
|
if (isGrounded) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
verticalSpeed = 1000f;
|
2025-09-25 14:57:01 +08:00
|
|
|
|
isGrounded = false;
|
2025-09-25 16:50:43 +08:00
|
|
|
|
System.out.println("跳跃高度: " + verticalSpeed);
|
2025-09-25 14:57:01 +08:00
|
|
|
|
changeAction(Action.JUMP);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void move(float x, float deltaTime) {
|
|
|
|
|
|
if (x != 0) {
|
|
|
|
|
|
isFacingRight = x > 0;
|
|
|
|
|
|
hitbox.x += x * speed * deltaTime;
|
|
|
|
|
|
changeAction(Action.MOVE); // 移动时切换为 MOVE 状态
|
|
|
|
|
|
} else if (isGrounded && !isAttacking) {
|
|
|
|
|
|
changeAction(Action.IDLE); // 停止移动时恢复待机
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 16:50:43 +08:00
|
|
|
|
private void updateAttackbox(String attackType) {
|
|
|
|
|
|
float offsetX;
|
|
|
|
|
|
float offsetY = 20; // 默认偏移量
|
|
|
|
|
|
float width = 80;
|
|
|
|
|
|
float height = 80;
|
|
|
|
|
|
|
|
|
|
|
|
switch (attackType) {
|
|
|
|
|
|
case "heavy":
|
|
|
|
|
|
offsetX = isFacingRight ? hitbox.width : -100;
|
|
|
|
|
|
offsetY = 40;
|
|
|
|
|
|
width = 100;
|
|
|
|
|
|
height = 100;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "light":
|
|
|
|
|
|
offsetX = isFacingRight ? hitbox.width - 10 : -attackbox.width + 10;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "special":
|
|
|
|
|
|
offsetX = isFacingRight ? hitbox.width + 20 : -attackbox.width - 20;
|
|
|
|
|
|
offsetY = 50;
|
|
|
|
|
|
width = 120;
|
|
|
|
|
|
height = 60;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
offsetX = isFacingRight ? hitbox.width - 10 : -attackbox.width + 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
attackbox.setPosition(hitbox.x + offsetX, hitbox.y + offsetY);
|
|
|
|
|
|
attackbox.setSize(width, height);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 14:57:01 +08:00
|
|
|
|
public void attack(String attackType) {
|
2025-09-25 16:50:43 +08:00
|
|
|
|
isAttacking = true;
|
|
|
|
|
|
changeAction(Action.ATTACK);
|
|
|
|
|
|
updateAttackbox(attackType); // 根据攻击类型更新攻击盒位置
|
2025-09-25 14:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void takeHit(int damage) {
|
|
|
|
|
|
health = Math.max(0, health - damage); // 扣除生命值,最小为 0
|
|
|
|
|
|
changeAction(health > 0 ? Action.HIT : Action.DEAD); // 根据生命值切换为受击或死亡状态
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isAlive() {
|
|
|
|
|
|
return health > 0; // 判断角色是否存活
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isAttacking() {
|
|
|
|
|
|
return isAttacking; // 判断是否处于攻击状态
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 常用访问器
|
|
|
|
|
|
public Rectangle getHitbox() {
|
|
|
|
|
|
return hitbox; // 获取碰撞盒
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Rectangle getAttackbox() {
|
|
|
|
|
|
return attackbox; // 获取攻击盒
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public int getHealth() {
|
|
|
|
|
|
return health; // 获取当前生命值
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getName() {
|
|
|
|
|
|
return name; // 获取角色名称
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setPosition(float x, float y) {
|
|
|
|
|
|
hitbox.setPosition(x, y); // 设置角色位置
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|