新增角色资源并更新战斗控制器

新增爱丽丝角色的PNG素材资源

引入用于管理战斗动作的战斗控制器类

移除旧版路径中的过时战斗控制器类

新增爱丽丝动画测试类用于验证动画效果

更新战斗角色类并新增多项功能

调整游戏核心类以集成新角色机制
This commit is contained in:
2025-09-22 11:37:04 +08:00
parent 78cf5ffb1b
commit 053d29398e
34 changed files with 2169 additions and 2191 deletions

View File

@@ -1,43 +1,47 @@
package uno.mloluyu.FighterController;
package uno.mloluyu.Controller;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.Array;
import uno.mloluyu.characters.Fighter;
/**
* 角色控制器处理玩家输入并映射到角色动作
*/
public class FighterController extends InputAdapter {
private final Fighter fighter;
private final Array<Integer> pressedKeys = new Array<>();
// 输入缓冲时间防止按键重复触发
private static final float INPUT_BUFFER = 0.2f;
private float attackBufferTimer = 0;
private float jumpBufferTimer = 0;
// 输入缓冲时间防止快速按键导致的重复触发
private static final float INPUT_DELAY = 0.2f;
private float attackCooldown = 0;
private float jumpCooldown = 0;
public FighterController(Fighter fighter) {
this.fighter = fighter;
}
/**
* 更新角色状态和输入缓冲
* 更新控制器状态和冷却时间
* @param deltaTime 帧间隔时间
*/
public void update(float deltaTime) {
// 更新输入缓冲计时器
if (attackBufferTimer > 0) attackBufferTimer -= deltaTime;
if (jumpBufferTimer > 0) jumpBufferTimer -= deltaTime;
// 更新冷却时间
if (attackCooldown > 0) attackCooldown -= deltaTime;
if (jumpCooldown > 0) jumpCooldown -= deltaTime;
// 处理移动输入
handleMovementInput(deltaTime);
handleMovement();
}
/**
* 处理移动输入
*/
private void handleMovementInput(float deltaTime) {
private void handleMovement() {
float moveX = 0;
// 检查按键状态
// 左右移动控制支持方向键和WSAD
if (isKeyPressed(Input.Keys.RIGHT) || isKeyPressed(Input.Keys.D)) {
moveX += 1;
}
@@ -46,7 +50,7 @@ public class FighterController extends InputAdapter {
}
// 调用角色移动方法
fighter.move(moveX, deltaTime);
fighter.move(moveX, Gdx.graphics.getDeltaTime());
}
/**
@@ -58,25 +62,32 @@ public class FighterController extends InputAdapter {
pressedKeys.add(keycode);
}
// 攻击按键
if ((keycode == Input.Keys.Z || keycode == Input.Keys.J) && attackBufferTimer <= 0) {
fighter.attack(1); // 普通攻击
attackBufferTimer = INPUT_BUFFER;
// 普通攻击Z键或J键
if ((keycode == Input.Keys.Z || keycode == Input.Keys.J) && attackCooldown <= 0) {
fighter.attack(1);
attackCooldown = INPUT_DELAY;
return true;
}
// 特殊攻击
if ((keycode == Input.Keys.X || keycode == Input.Keys.K) && attackBufferTimer <= 0) {
fighter.attack(4); // 特殊攻击1
attackBufferTimer = INPUT_BUFFER;
// 第二攻击X键或K键
if ((keycode == Input.Keys.X || keycode == Input.Keys.K) && attackCooldown <= 0) {
fighter.attack(2);
attackCooldown = INPUT_DELAY;
return true;
}
// 跳跃
if ((keycode == Input.Keys.SPACE || keycode == Input.Keys.W || keycode == Input.Keys.UP) && jumpBufferTimer <= 0) {
// 跳跃空格上方向键或W键
if ((keycode == Input.Keys.SPACE || keycode == Input.Keys.UP || keycode == Input.Keys.W) && jumpCooldown <= 0) {
// 这里假设你已经实现了跳跃方法
// fighter.jump();
jumpBufferTimer = INPUT_BUFFER;
jumpCooldown = INPUT_DELAY;
return true;
}
// 防御左Shift或右Shift
if (keycode == Input.Keys.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT) {
fighter.changeAction(Fighter.Action.DEFEND);
return true;
}
@@ -89,27 +100,28 @@ public class FighterController extends InputAdapter {
@Override
public boolean keyUp(int keycode) {
pressedKeys.removeValue(keycode, false);
// 释放防御键时恢复到 idle 状态
if ((keycode == Input.Keys.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT) &&
fighter.getCurrentAction() == Fighter.Action.DEFEND) {
fighter.changeAction(Fighter.Action.IDLE);
}
return false;
}
/**
* 检查按键是否被按下
* 检查按键是否处于按下状态
*/
private boolean isKeyPressed(int keycode) {
return pressedKeys.contains(keycode, false);
}
/**
* 绘制角色
*/
public void render(SpriteBatch batch) {
fighter.render(batch);
}
/**
* 获取控制的角色
* 获取当前控制的角色
*/
public Fighter getFighter() {
return fighter;
}
}
}

View File

@@ -0,0 +1,117 @@
package uno.mloluyu.characters;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import uno.mloluyu.characters.Alice;
import uno.mloluyu.characters.Fighter;
public class AliceAnimationTest extends ApplicationAdapter {
private SpriteBatch batch;
private OrthographicCamera camera;
private Viewport viewport;
private Alice alice;
private float stateTimer; // 用于切换动作测试
@Override
public void create() {
// 初始化相机和批处理
camera = new OrthographicCamera();
viewport = new FitViewport(800, 600, camera);
batch = new SpriteBatch();
// 创建 Alice 实例
alice = new Alice();
// 初始位置
alice.getHitbox().setPosition(
viewport.getWorldWidth() / 2 - alice.getHitbox().width / 2,
viewport.getWorldHeight() / 2 - alice.getHitbox().height / 2
);
stateTimer = 0;
}
@Override
public void render() {
// 清屏
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 更新状态时间
float delta = Gdx.graphics.getDeltaTime();
stateTimer += delta;
// 控制 Alice 执行不同动作,测试动画切换
controlAliceActions();
// 更新 Alice
alice.update(delta);
// 绘制
batch.setProjectionMatrix(camera.combined);
batch.begin();
alice.render(batch);
batch.end();
}
/**
* 自动切换 Alice 动作,测试各种动画
*/
private void controlAliceActions() {
// 每2秒切换一个动作
if (stateTimer < 2) {
alice.changeAction(Fighter.Action.IDLE);
} else if (stateTimer < 4) {
alice.move(1, Gdx.graphics.getDeltaTime()); // 向右走
} else if (stateTimer < 6) {
alice.changeAction(Fighter.Action.JUMP);
} else if (stateTimer < 8) {
alice.attack(1); // 普通攻击1
} else if (stateTimer < 10) {
alice.attack(2); // 普通攻击2
} else if (stateTimer < 12) {
alice.attack(3); // 普通攻击3
} else if (stateTimer < 14) {
alice.takeHit(10); // 受击
} else {
// 循环
stateTimer = 0;
alice.getHitbox().setPosition(
viewport.getWorldWidth() / 2 - alice.getHitbox().width / 2,
viewport.getWorldHeight() / 2 - alice.getHitbox().height / 2
);
}
}
@Override
public void resize(int width, int height) {
viewport.update(width, height);
}
@Override
public void dispose() {
batch.dispose();
alice.dispose();
}
// 直接运行这个 main 方法即可启动测试
// public static void main(String[] args) {
// Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
// config.setTitle("Alice Animation Test");
// config.setWindowedMode(800, 600);
// config.setForegroundFPS(60);
// config.useVsync(true);
// new Lwjgl3Application(new AliceAnimationTest(), config);
// }
}

View File

@@ -49,8 +49,8 @@ public abstract class Fighter implements Disposable {
// 精灵图表
protected TextureAtlas atlas;
// 缩放比例
protected float scaleX = 3.0f;
protected float scaleY = 3.0f;
protected float scaleX = 1.0f;
protected float scaleY = 1.0f;
@SuppressWarnings("unchecked")
public Fighter(TextureAtlas atlas) {
@@ -195,32 +195,38 @@ public abstract class Fighter implements Disposable {
return;
}
float x = hitbox.x;
float y = hitbox.y;
float width = hitbox.width;
float height = hitbox.height;
// 1. 计算缩放后的帧尺寸(保持原始比例)
float frameWidth = currentFrame.getRegionWidth() * scaleX;
float frameHeight = currentFrame.getRegionHeight() * scaleY;
if (isFacingRight) {
// 正向绘制并应用缩放
batch.draw(
currentFrame,
x, y, // 位置
width / 2, height / 2, // 缩放原点(中心)
width, height, // 宽高
scaleX, scaleY, // 缩放比例
0 // 旋转角度
);
} else {
// 翻转绘制并应用缩放
batch.draw(
currentFrame,
x, y, // 位置
width / 2, height / 2, // 缩放原点(中心
width, height, // 宽高
-scaleX, scaleY, // X轴翻转并应用缩放
0 // 旋转角度
);
}
// 2. 计算绘制位置始终以hitbox为基准水平居中、底部对齐关键
// 无论是否翻转x/y坐标都基于hitbox计算保证位置锚点一致
float drawX = hitbox.x + (hitbox.width - frameWidth) / 2; // 水平居中hitbox中心和帧中心对齐
float drawY = hitbox.y; // 底部对齐hitbox底部和帧底部对齐
// 3. 处理翻转用TextureRegion的flip方法避免手动偏移x坐标
// 先记录原始flip状态防止影响其他地方复用该帧
boolean wasFlippedX = currentFrame.isFlipX();
// 根据朝向设置翻转只翻转X轴Y轴不变
currentFrame.flip(!isFacingRight && !wasFlippedX, false); // 正向→不翻,反向→翻
currentFrame.flip(isFacingRight && wasFlippedX, false); // 修复原始已翻转的情况
// 4. 绘制:缩放中心为帧的中心,确保翻转/旋转时围绕自身中心
batch.draw(
currentFrame,
drawX, // 绘制X基于hitbox的居中位置固定不变
drawY, // 绘制Y基于hitbox的底部固定不变
frameWidth / 2, // 缩放/旋转中心X帧的中心
frameHeight / 2, // 缩放/旋转中心Y帧的中心
frameWidth, // 缩放后的宽度已乘scaleX
frameHeight, // 缩放后的高度已乘scaleY
1f, // X轴额外缩放这里已提前计算设为1避免重复缩放
1f, // Y轴额外缩放同上
0f // 旋转角度
);
// 5. 恢复帧的原始flip状态关键避免影响后续绘制
currentFrame.flip(wasFlippedX != currentFrame.isFlipX(), false);
}
/**

View File

@@ -4,18 +4,21 @@ import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import uno.mloluyu.Controller.FighterController;
import uno.mloluyu.characters.Alice;
public class GameCore implements ApplicationListener {
private SpriteBatch batch;
private Alice alice1;
private FighterController controller;
@Override
public void create() {
batch = new SpriteBatch();
alice1= new Alice();
controller = new FighterController(alice1);
Gdx.input.setInputProcessor(controller);
}
@@ -24,8 +27,8 @@ public class GameCore implements ApplicationListener {
Gdx.gl.glClearColor(150, 150, 150, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
alice1.update(Gdx.graphics.getDeltaTime());
controller.update(Gdx.graphics.getDeltaTime());
batch.begin();
alice1.render(batch);
batch.end();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB