This commit is contained in:
2025-09-26 09:31:46 +08:00
parent f5d5939f29
commit 4f486b367f
64 changed files with 758 additions and 153 deletions

View File

@@ -0,0 +1,20 @@
package uno.mloluyu.characters;
/** 提供状态切换验证。 */
public final class ActionStateGuard {
private ActionStateGuard() {
}
public static Action transition(FighterBase fighter, Action next) {
Action current = fighter.getCurrentAction();
if (current == next)
return current;
if (!ActionTransitionMap.can(current, next)) {
return current;
}
if (fighter instanceof uno.mloluyu.characters.SimpleFighter sf) {
sf.directSetAction(next);
}
return next;
}
}

View File

@@ -0,0 +1,25 @@
package uno.mloluyu.characters;
import java.util.*;
/** 定义合法动作迁移,供校验/日志使用。 */
public final class ActionTransitionMap {
private static final Map<Action, Set<Action>> ALLOWED = new EnumMap<>(Action.class);
static {
allow(Action.IDLE, Action.MOVE, Action.JUMP, Action.ATTACK, Action.DEFEND, Action.HIT, Action.DEAD);
allow(Action.MOVE, Action.IDLE, Action.JUMP, Action.ATTACK, Action.HIT, Action.DEAD);
allow(Action.JUMP, Action.HIT, Action.ATTACK, Action.DEAD, Action.IDLE, Action.MOVE);
allow(Action.ATTACK, Action.HIT, Action.DEAD, Action.IDLE, Action.MOVE);
allow(Action.DEFEND, Action.HIT, Action.IDLE, Action.MOVE, Action.DEAD);
allow(Action.HIT, Action.IDLE, Action.MOVE, Action.DEAD);
allow(Action.DEAD); // 终止状态
}
private static void allow(Action from, Action... tos) {
ALLOWED.put(from, new HashSet<>(Arrays.asList(tos)));
}
public static boolean can(Action from, Action to) {
return ALLOWED.getOrDefault(from, Collections.emptySet()).contains(to);
}
}

View File

@@ -3,14 +3,12 @@ package uno.mloluyu.characters;
public class AdvancedFighter extends SimpleFighter {
public AdvancedFighter(String name) {
super(name); // 调用父类构造函数
super(name);
}
@Override
public void attack(String attackType) {
// 先使用父类的攻击逻辑来保证 isAttacking/attackTimer/attackbox 等状态被正确设置
super.attack(attackType);
// 在这里可以添加 AdvancedFighter 特有的扩展行为(攻击力、特效等)
// 例如:根据 attackType 调整伤害或触发粒子/声音,但不要忘记保留父类的状态设置
}
}

View File

@@ -0,0 +1,47 @@
package uno.mloluyu.characters;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.graphics.Color;
import java.util.concurrent.ThreadLocalRandom;
/** 公共角色基类:后续 Simple/Advanced 统一继承。 */
public abstract class FighterBase {
protected String name;
protected Action currentAction = Action.IDLE;
protected Rectangle hitbox = new Rectangle(0, 0, 64, 128);
protected Rectangle attackbox = new Rectangle(0, 0, 80, 80);
protected final Color debugColor;
public FighterBase(String name) {
this.name = name;
ThreadLocalRandom r = ThreadLocalRandom.current();
float rr = 0.35f + r.nextFloat() * 0.65f;
float gg = 0.35f + r.nextFloat() * 0.65f;
float bb = 0.35f + r.nextFloat() * 0.65f;
this.debugColor = new Color(rr, gg, bb, 1f);
}
public Action getCurrentAction() {
return currentAction;
}
public Rectangle getHitbox() {
return hitbox;
}
public Rectangle getAttackbox() {
return attackbox;
}
public String getName() {
return name;
}
public Color getDebugColor() {
return debugColor;
}
protected void setAction(Action next) {
this.currentAction = next;
}
}

View File

@@ -10,21 +10,21 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import uno.mloluyu.network.NetworkManager;
import uno.mloluyu.util.GameConstants;
/**
* 简化版角色类,仅包含移动、攻击、受击等基础功能。
*/
public class SimpleFighter {
public class SimpleFighter extends FighterBase {
private String name; // 角色名称
private Action currentAction = Action.IDLE; // 当前动作状态
// 继承: name, currentAction, hitbox, attackbox
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 float speed = GameConstants.MOVE_SPEED; // 水平移动速度
private int health = 200; // 生命值
private boolean isAttacking = false; // 是否正在攻击
private boolean attackJustStarted = false; // 攻击是否刚开始
private float attackTimer = 0f; // 攻击计时器
@@ -41,7 +41,7 @@ public class SimpleFighter {
private static final float KNOCKBACK_DURATION = 0.12f;
public SimpleFighter(String name) {
this.name = name;
super(name);
}
public void update(float deltaTime) {
@@ -76,10 +76,10 @@ public class SimpleFighter {
}
if (!isGrounded) {
verticalSpeed -= 2500 * deltaTime;
verticalSpeed -= GameConstants.GRAVITY * deltaTime;
hitbox.y += verticalSpeed * deltaTime;
if (hitbox.y <= 0) {
hitbox.y = 0;
if (hitbox.y <= GameConstants.GROUND_Y) {
hitbox.y = GameConstants.GROUND_Y;
verticalSpeed = 0;
isGrounded = true;
changeAction(Action.IDLE);
@@ -103,7 +103,7 @@ public class SimpleFighter {
hitbox.y + hitbox.height * 0.7f);
}
public void handleInput(int keycode, boolean isPressed) {
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());
@@ -138,12 +138,16 @@ public class SimpleFighter {
}
public void changeAction(Action newAction) {
this.currentAction = newAction;
this.currentAction = ActionStateGuard.transition(this, newAction);
}
void directSetAction(Action a) {
this.currentAction = a;
}
public void jump() {
if (isGrounded) {
verticalSpeed = 1000f;
verticalSpeed = GameConstants.JUMP_SPEED;
isGrounded = false;
changeAction(Action.JUMP);
}
@@ -181,28 +185,32 @@ public class SimpleFighter {
}
private void updateAttackbox(String attackType) {
float offsetX, offsetY = 20, width = 80, height = 80;
float baseOffsetY = 20f;
float width = 80f, height = 80f;
float offsetX;
float offsetY = baseOffsetY;
// 先决定尺寸,再根据朝向计算 offset不再用旧 attackbox.width 避免漂移)
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;
width = 100f;
height = 100f;
offsetY = 40f;
offsetX = isFacingRight ? hitbox.width : -width; // 重击靠近身体或覆盖前方
break;
case "special":
offsetX = isFacingRight ? hitbox.width + 20 : -attackbox.width - 20;
offsetY = 50;
width = 120;
height = 60;
width = 120f;
height = 60f;
offsetY = 50f;
offsetX = isFacingRight ? hitbox.width + 20f : -width - 20f;
break;
case "light":
default:
offsetX = isFacingRight ? hitbox.width - 10 : -attackbox.width + 10;
// 轻击稍微往前,不再参考旧 attackbox.width
offsetX = isFacingRight ? hitbox.width - 10f : -80f + 10f;
break;
}
attackbox.setPosition(hitbox.x + offsetX, hitbox.y + offsetY);
attackbox.setSize(width, height);
attackbox.setPosition(hitbox.x + offsetX, hitbox.y + offsetY);
}
public void attack(String attackType) {
@@ -304,6 +312,13 @@ public class SimpleFighter {
hitbox.setPosition(x, y);
}
/** 若当前 Y 低于地面则贴到地面。 */
public void alignToGround() {
if (hitbox.y < GameConstants.GROUND_Y) {
hitbox.y = GameConstants.GROUND_Y;
}
}
public float getAttackTimer() {
return attackTimer;
}