优化
This commit is contained in:
20
src/main/java/uno/mloluyu/characters/ActionStateGuard.java
Normal file
20
src/main/java/uno/mloluyu/characters/ActionStateGuard.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 调整伤害或触发粒子/声音,但不要忘记保留父类的状态设置
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
47
src/main/java/uno/mloluyu/characters/FighterBase.java
Normal file
47
src/main/java/uno/mloluyu/characters/FighterBase.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user