Merge remote-tracking branch 'origin/test' into test

This commit is contained in:
2025-09-25 18:49:12 +08:00
22 changed files with 369 additions and 215 deletions

View File

@@ -12,15 +12,15 @@ public class AdvancedFighter extends SimpleFighter {
switch (attackType.toLowerCase()) {
case "light":
changeAction(Action.ATTACK);
System.out.println(getName() + " 发起轻攻击!");
// System.out.println(getName() + " 发起轻攻击!");
break;
case "heavy":
changeAction(Action.ATTACK);
System.out.println(getName() + " 发起重攻击!");
// System.out.println(getName() + " 发起重攻击!");
break;
case "special":
changeAction(Action.ATTACK);
System.out.println(getName() + " 发动特殊技能!");
// System.out.println(getName() + " 发动特殊技能!");
break;
default:
super.attack(attackType); // 默认调用父类攻击逻辑

View File

@@ -1,7 +1,9 @@
package uno.mloluyu.characters;
import java.util.HashMap;
import java.util.Map;
// 注意:本类使用的是包 uno.mloluyu.characters 下的 Action (IDLE, JUMP, MOVE, ATTACK, DEFEND, HIT, DEAD)
// 避免与 uno.mloluyu.characters.character.Action (ATTACK1/2/3...) 混淆
// 简化:去除内部按键时长跟踪,统一由 FighterController 负责
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
@@ -16,7 +18,6 @@ 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; // 垂直速度(可用于跳跃或下落)
@@ -28,36 +29,60 @@ public class SimpleFighter {
private float speed = 300f; // 移动速度(像素/秒)
private int health = 100; // 当前生命值
private int attackPower = 10; // 攻击力(暂未使用)
private boolean isAttacking = false; // 是否正在攻击(攻击状态标志)
private boolean attackJustStarted = false; // 攻击刚开始的标记,避免第一帧被减掉
private int attackInvokeCount = 0; // 调试attack()调用次数
private SimpleFighter fighter; // 添加 fighter 的声明
// 攻击持续时间(秒)
private float attackTimer = 0f;
private static final float ATTACK_DURATION = 0.15f; // 攻击判定显示时间
private Iterable<Integer> pressedKeys = new HashMap<Integer, Float>().keySet(); // 初始化 pressedKeys
private static boolean debugEnabled = true; // F3 开关
public static void toggleDebug() {
debugEnabled = !debugEnabled;
}
public static boolean isDebugEnabled() {
return debugEnabled;
}
public SimpleFighter(String name) {
this.name = name; // 构造函数,初始化角色名称
this.fighter = this; // 初始化 fighter 为当前实例
}
public void update(float deltaTime) {
updateAttackbox();
// 自愈:动作是 ATTACK 但标记丢失
if (currentAction == Action.ATTACK && attackTimer > 0f && !isAttacking) {
isAttacking = true;
attackJustStarted = false;
}
// 攻击只持续一帧
// 攻击计时
if (isAttacking) {
isAttacking = false;
changeAction(Action.IDLE);
if (attackJustStarted) {
attackJustStarted = false; // 第一帧不扣时间
} else {
attackTimer -= deltaTime;
}
if (attackTimer <= 0f) {
isAttacking = false;
attackTimer = 0f;
if (currentAction == Action.ATTACK)
changeAction(Action.IDLE);
if (debugEnabled)
System.out.println("[ATTACK-END]");
}
} else {
// 空闲/移动状态下保持一个默认攻击盒(便于调试观察)
updateAttackbox("light");
}
for (int keycode : pressedKeys) {
keyPressDuration.put(keycode, keyPressDuration.getOrDefault(keycode, 0f) + deltaTime); // 更新持续时间
fighter.handleInput(keycode, true); // 持续按下的键
}
// 垂直移动(跳跃或重力)
if (!isGrounded) {
verticalSpeed -= 980 * deltaTime; // 简单重力模拟
hitbox.y += verticalSpeed * deltaTime;
// 垂直运动 & 重力
if (!isGrounded) {
verticalSpeed -= 2500 * deltaTime;
hitbox.y += verticalSpeed * deltaTime;
if (hitbox.y <= 0) {
hitbox.y = 0;
verticalSpeed = 0;
@@ -67,50 +92,59 @@ public class SimpleFighter {
}
}
@Deprecated
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); // 开始绘制线框
batch.end();
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
renderDebug(shapeRenderer);
shapeRenderer.end();
batch.begin();
}
shapeRenderer.setColor(Color.BLUE); // 设置颜色为蓝色
shapeRenderer.rect(hitbox.x, hitbox.y, hitbox.width, hitbox.height); // 绘制碰撞盒
public void renderSprite(SpriteBatch batch) {
/* 预留贴图渲染入口 */ }
public void renderDebug(ShapeRenderer sr) {
if (!debugEnabled)
return;
sr.setColor(Color.BLUE);
sr.rect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
if (isAttacking) {
shapeRenderer.setColor(Color.RED); // 设置颜色为红色
shapeRenderer.rect(attackbox.x, attackbox.y, attackbox.width, attackbox.height); // 绘制攻击盒
// 只画轮廓;填充在 GameScreen 专门的 Filled pass 中画
sr.setColor(Color.RED);
sr.rect(attackbox.x, attackbox.y, attackbox.width, attackbox.height);
}
shapeRenderer.end(); // 结束 ShapeRenderer 渲染
batch.begin(); // 恢复 SpriteBatch 渲染
// 朝向箭头
float arrowX = isFacingRight ? hitbox.x + hitbox.width + 5 : hitbox.x - 15;
sr.setColor(Color.YELLOW);
sr.line(arrowX, hitbox.y + hitbox.height * 0.7f, arrowX + (isFacingRight ? 10 : -10),
hitbox.y + hitbox.height * 0.7f);
}
public void handleInput(int keycode, boolean isPressed, float duration) {
// 根据按键和按下状态处理输入行为
if (isPressed) {
if (keycode == Input.Keys.LEFT || keycode == Input.Keys.A) {
move(-1, Gdx.graphics.getDeltaTime()); // 向左移动
move(-1, Gdx.graphics.getDeltaTime());
} else if (keycode == Input.Keys.RIGHT || keycode == Input.Keys.D) {
move(1, Gdx.graphics.getDeltaTime()); // 向右移动
}
if (keycode == Input.Keys.Z || keycode == Input.Keys.J) {
attack(""); // 普通攻击
} else if (keycode == Input.Keys.X || keycode == Input.Keys.K) {
attack(""); // 重攻击(暂未区分)
} else if (keycode == Input.Keys.SPACE || keycode == Input.Keys.UP || keycode == Input.Keys.W) {
attack(""); // 跳跃(暂未实现跳跃逻辑)
} else if (keycode == Input.Keys.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT) {
attack(""); // 防御(暂未实现防御逻辑)
move(1, Gdx.graphics.getDeltaTime());
}
if (keycode == Input.Keys.SPACE || keycode == Input.Keys.UP || keycode == Input.Keys.W) {
System.out.println("点击了跳跃");
jump();
}
// 攻击按键
if (!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.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT) &&
getCurrentAction() == Action.DEFEND) {
if ((keycode == Input.Keys.LEFT || keycode == Input.Keys.RIGHT || keycode == Input.Keys.A
|| keycode == Input.Keys.D) &&
getCurrentAction() == Action.MOVE) {
changeAction(Action.IDLE);
}
}
@@ -120,16 +154,6 @@ public class SimpleFighter {
handleInput(keycode, isPressed, 0f); // 调用已有方法,补充默认持续时间
}
public void handleRelease(int keycode, float duration) {
// 处理按键释放逻辑
System.out.println("按键释放: " + keycode + ", 持续时间: " + duration);
keyPressDuration.remove(keycode);
if (keycode == Input.Keys.LEFT || keycode == Input.Keys.RIGHT || keycode == Input.Keys.A
|| keycode == Input.Keys.D) {
changeAction(Action.IDLE);
}
}
public Action getCurrentAction() {
return currentAction; // 获取当前动作状态
}
@@ -140,8 +164,9 @@ public class SimpleFighter {
public void jump() {
if (isGrounded) {
verticalSpeed = 600f;
verticalSpeed = 1000f;
isGrounded = false;
System.out.println("跳跃高度: " + verticalSpeed);
changeAction(Action.JUMP);
}
}
@@ -156,9 +181,45 @@ public class SimpleFighter {
}
}
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);
}
public void attack(String attackType) {
isAttacking = true; // 设置攻击状态
changeAction(Action.ATTACK); // 切换为攻击动作
isAttacking = true;
attackTimer = ATTACK_DURATION;
attackJustStarted = true;
changeAction(Action.ATTACK);
updateAttackbox(attackType);
attackInvokeCount++;
if (debugEnabled)
System.out.println("[ATTACK] type=" + attackType + " count=" + attackInvokeCount);
}
public void takeHit(int damage) {
@@ -174,12 +235,6 @@ public class SimpleFighter {
return isAttacking; // 判断是否处于攻击状态
}
private void updateAttackbox() {
// 根据朝向更新攻击盒位置,使其位于角色前方
float offsetX = isFacingRight ? hitbox.width - 10 : -attackbox.width + 10;
attackbox.setPosition(hitbox.x + offsetX, hitbox.y + 20);
}
// 常用访问器
public Rectangle getHitbox() {
return hitbox; // 获取碰撞盒
@@ -200,4 +255,21 @@ public class SimpleFighter {
public void setPosition(float x, float y) {
hitbox.setPosition(x, y); // 设置角色位置
}
public void debugPrintState() {
if (debugEnabled)
System.out.println("[STATE] action=" + currentAction + ", atk=" + isAttacking + ", t=" + attackTimer);
}
public float getAttackTimer() {
return attackTimer;
}
public float getAttackTimerPercent() {
return isAttacking ? attackTimer / ATTACK_DURATION : 0f;
}
public int getAttackInvokeCount() {
return attackInvokeCount;
}
}

View File

@@ -92,7 +92,7 @@ public abstract class Fighter implements Disposable {
return;
switch (currentAction) {
case ATTACK1, ATTACK2, ATTACK3, SPECIAL1, SPECIAL2, HIT -> changeAction(Action.IDLE);
case ATTACK1, ATTACK2, ATTACK3, ATTACK4, SPECIAL1, SPECIAL2, HIT -> changeAction(Action.IDLE);
case JUMP -> changeAction(Action.FALL);
default -> {
}

View File

@@ -3,11 +3,9 @@ package uno.mloluyu.characters.character;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import uno.mloluyu.characters.character.Fighter.Action;
public class Reimu extends Fighter {
public Reimu() {
super(new TextureAtlas(Gdx.files.internal("src\\main\\resources\\character\\reimu\\reimu.atlas")));
super("Reimu", new TextureAtlas(Gdx.files.internal("src/main/resources/character/reimu/reimu.atlas")));
// 设置角色属性
speed = 350f; // 更快的移动速度
@@ -18,27 +16,27 @@ public class Reimu extends Fighter {
@Override
protected void loadAnimations() {
// TODO Auto-generated method stub
// 加载基础动作动画
loadAnimationFromAtlas(Action.IDLE, "other/stand", 9, true);
loadAnimationFromAtlas(Action.WALK, "other/walkFront", 9, true);
loadAnimationFromAtlas(Action.JUMP, "other/jump", 8, false);
loadAnimationFromAtlas(Action.FALL, "other/hitSpin", 5, false);
// 基础动作 (looping)
animationManager.loadLooping(Action.IDLE, "other/stand", 9);
animationManager.loadLooping(Action.WALK, "other/walkFront", 9);
// 一次性动作 (one-shot)
animationManager.loadOneShot(Action.JUMP, "other/jump", 8);
animationManager.loadOneShot(Action.FALL, "other/hitSpin", 5);
// 加载攻击动作动画
loadAnimationFromAtlas(Action.ATTACK1, "attackAa/attackAa", 6, false);
loadAnimationFromAtlas(Action.ATTACK2, "attackAb/attackAb", 6, false);
loadAnimationFromAtlas(Action.ATTACK3, "attackAc/attackAc", 6, false);
loadAnimationFromAtlas(Action.ATTACK4, "attackAd/attackAd", 6, false);
// 攻击动作
animationManager.loadOneShot(Action.ATTACK1, "attackAa/attackAa", 6);
animationManager.loadOneShot(Action.ATTACK2, "attackAb/attackAb", 6);
animationManager.loadOneShot(Action.ATTACK3, "attackAc/attackAc", 6);
animationManager.loadOneShot(Action.ATTACK4, "attackAd/attackAd", 6);
// 加载受击动画
loadAnimationFromAtlas(Action.HIT, "hitSpin/hitSpin", 5, false);
// 受击
animationManager.loadOneShot(Action.HIT, "hitSpin/hitSpin", 5);
// 设置帧间隔(动作速度)
setFrameDuration(Action.IDLE, 0.04f);
setFrameDuration(Action.WALK, 0.08f);
setFrameDuration(Action.ATTACK1, 0.07f);
setFrameDuration(Action.SPECIAL2, 0.06f);
// 帧间隔
animationManager.setFrameDuration(Action.IDLE, 0.04f);
animationManager.setFrameDuration(Action.WALK, 0.08f);
animationManager.setFrameDuration(Action.ATTACK1, 0.07f);
animationManager.setFrameDuration(Action.SPECIAL2, 0.06f);
}
@Override