添加 UI 资源并重构角色类
添加了新的 UI 资源:logo.png、uiskin.atlas 和 uiskin.json,以改进界面设计。 移除了过时的 FighterController 和 GameCore 类,以精简代码库。 引入了新的角色类:FighterList 和 Reimu,增加了角色选择选项。 实现了新的桌面屏幕:CharacterSelectScreen(角色选择屏幕)、GameScreen(游戏屏幕)、MainMenuScreen(主菜单屏幕)和 StartScreen(开始屏幕),以改善用户导航。 通过新的 ConnectClient、ConnectServer 和 NetworkManager 类建立了网络功能。 更新了工具类:ClearScreen、Font 和 SimpleFormatter,以提升功能。 创建了新的 ButtonActions 类来处理按钮交互。
0
.trae/rules/project_rules.md
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"kingleo.qwen"
|
||||
]
|
||||
}
|
||||
31
pom.xml
@@ -20,6 +20,37 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx</artifactId>
|
||||
<version>1.12.1</version> <!-- 替换为你的 LibGDX 版本 -->
|
||||
</dependency>
|
||||
|
||||
<!-- FreeType 字体扩展 -->
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-freetype</artifactId>
|
||||
<version>1.12.1</version> <!-- 与 LibGDX 版本保持一致 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 桌面平台的 FreeType 原生库 -->
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-freetype-platform</artifactId>
|
||||
<version>1.12.1</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-backend-lwjgl</artifactId>
|
||||
<version>1.12.1</version> <!-- 使用你的 LibGDX 版本号 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-platform</artifactId>
|
||||
<version>1.12.1</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx</artifactId>
|
||||
|
||||
@@ -4,57 +4,73 @@ import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
|
||||
/**
|
||||
* 角色类,继承自Fighter父类
|
||||
* Alice角色类,继承自Fighter父类,定义其专属属性和动画
|
||||
*/
|
||||
public class Alice extends Fighter {
|
||||
private TextureAtlas atlas;
|
||||
|
||||
public Alice() {
|
||||
super(new TextureAtlas(Gdx.files.internal("src\\main\\resources\\character\\alice\\精灵1.2.atlas")));
|
||||
speed = 350f; // 速度更快
|
||||
maxHealth = 90; // 生命值较低
|
||||
health = maxHealth;
|
||||
attackPower = 12; // 攻击力中等\
|
||||
super(new TextureAtlas(Gdx.files.internal("src/main/resources/character/alice/精灵1.2.atlas")));
|
||||
|
||||
// 设置角色属性
|
||||
speed = 350f; // 更快的移动速度
|
||||
maxHealth = 90; // 较低的生命值
|
||||
health = maxHealth;
|
||||
attackPower = 12; // 中等攻击力
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAnimations() {
|
||||
// 加载基础动作动画
|
||||
loadAnimationFromAtlas(Action.IDLE, "stand/stand", 15, true);
|
||||
loadAnimationFromAtlas(Action.WALK, "walkFront/walkFront", 9, true);
|
||||
loadAnimationFromAtlas(Action.JUMP, "jump/jump", 8, false);
|
||||
loadAnimationFromAtlas(Action.FALL, "hitSpin/hitSpin", 5, false);
|
||||
|
||||
// 加载攻击动作动画
|
||||
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);
|
||||
|
||||
// 加载受击动画
|
||||
loadAnimationFromAtlas(Action.HIT, "hitSpin/hitSpin", 5, false);
|
||||
|
||||
// 为特定动作设置帧间隔
|
||||
// 设置帧间隔(动作速度)
|
||||
setFrameDuration(Action.IDLE, 0.04f);
|
||||
setFrameDuration(Action.WALK, 0.08f); // 行走更快
|
||||
setFrameDuration(Action.ATTACK1, 0.07f); // 攻击更快
|
||||
setFrameDuration(Action.SPECIAL2, 0.06f); // 特殊技能2非常快
|
||||
setFrameDuration(Action.WALK, 0.08f);
|
||||
setFrameDuration(Action.ATTACK1, 0.07f);
|
||||
setFrameDuration(Action.SPECIAL2, 0.06f);
|
||||
}
|
||||
|
||||
// 特定的移动状态处理
|
||||
/**
|
||||
* 特定移动状态处理:允许跳跃状态下移动
|
||||
*/
|
||||
@Override
|
||||
protected void handleMoveState() {
|
||||
// 在跳跃时也能移动
|
||||
if (currentAction != Action.ATTACK1 && currentAction != Action.ATTACK2 &&
|
||||
currentAction != Action.ATTACK3 && currentAction != Action.SPECIAL1 &&
|
||||
currentAction != Action.SPECIAL2 && currentAction != Action.DEFEND) {
|
||||
|
||||
if (currentAction != Action.JUMP && currentAction != Action.FALL) {
|
||||
changeAction(Action.WALK);
|
||||
}
|
||||
if (currentAction != Action.ATTACK1 &&
|
||||
currentAction != Action.ATTACK2 &&
|
||||
currentAction != Action.ATTACK3 &&
|
||||
currentAction != Action.SPECIAL1 &&
|
||||
currentAction != Action.SPECIAL2 &&
|
||||
currentAction != Action.DEFEND &&
|
||||
currentAction != Action.JUMP &&
|
||||
currentAction != Action.FALL) {
|
||||
changeAction(Action.WALK);
|
||||
}
|
||||
}
|
||||
|
||||
// 可以在空中攻击
|
||||
/**
|
||||
* 空中也可以攻击
|
||||
*/
|
||||
@Override
|
||||
protected boolean canAttack() {
|
||||
return super.canAttack() || currentAction == Action.JUMP || currentAction == Action.FALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前生命值
|
||||
*/
|
||||
public int getHp() {
|
||||
return health;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
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);
|
||||
// }
|
||||
}
|
||||
@@ -11,10 +11,13 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
|
||||
import java.util.EnumMap;
|
||||
|
||||
/**
|
||||
* 格斗角色父类,封装所有角色共有的动画和状态管理逻辑
|
||||
*/
|
||||
public abstract class Fighter implements Disposable {
|
||||
|
||||
public enum Action {
|
||||
IDLE, WALK, JUMP, FALL,
|
||||
ATTACK1, ATTACK2, ATTACK3, ATTACK4,
|
||||
@@ -22,328 +25,198 @@ public abstract class Fighter implements Disposable {
|
||||
SPECIAL1, SPECIAL2,
|
||||
DEATH
|
||||
}
|
||||
protected String name;
|
||||
|
||||
// 画帧间隔(秒)
|
||||
protected static final float DEFAULT_FRAME_DURATION = 0.1f;
|
||||
protected float[] frameDurations;
|
||||
protected static final int DEFAULT_HEALTH = 100;
|
||||
protected static final float DEFAULT_SPEED = 300f;
|
||||
|
||||
// 当前状态
|
||||
protected Action currentAction;
|
||||
protected float stateTime;
|
||||
protected boolean isFacingRight;
|
||||
protected boolean isAnimationFinished;
|
||||
protected String name;
|
||||
protected Action currentAction = Action.IDLE;
|
||||
protected float stateTime = 0f;
|
||||
protected boolean isFacingRight = true;
|
||||
protected boolean isAnimationFinished = false;
|
||||
|
||||
// 动画集合
|
||||
protected Animation<TextureRegion>[] animations;
|
||||
protected EnumMap<Action, Animation<TextureRegion>> animations = new EnumMap<>(Action.class);
|
||||
protected EnumMap<Action, Float> frameDurations = new EnumMap<>(Action.class);
|
||||
|
||||
// 碰撞检测
|
||||
protected Rectangle hitbox;
|
||||
protected Rectangle attackbox;
|
||||
protected Rectangle hitbox = new Rectangle(0, 0, 64, 128);
|
||||
protected Rectangle attackbox = new Rectangle(0, 0, 80, 80);
|
||||
|
||||
// 角色属性
|
||||
protected float speed;
|
||||
protected int health;
|
||||
protected int maxHealth;
|
||||
protected int attackPower;
|
||||
protected float speed = DEFAULT_SPEED;
|
||||
protected int health = DEFAULT_HEALTH;
|
||||
protected int maxHealth = DEFAULT_HEALTH;
|
||||
protected int attackPower = 10;
|
||||
|
||||
// 精灵图表
|
||||
protected TextureAtlas atlas;
|
||||
// 缩放比例
|
||||
protected float scaleX = 1.0f;
|
||||
protected float scaleY = 1.0f;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Fighter() {
|
||||
}
|
||||
|
||||
public Fighter(TextureAtlas atlas) {
|
||||
this.atlas = atlas;
|
||||
int actionCount = Action.values().length;
|
||||
|
||||
animations = new Animation[actionCount];
|
||||
frameDurations = new float[actionCount];
|
||||
|
||||
for (int i = 0; i < actionCount; i++) {
|
||||
frameDurations[i] = DEFAULT_FRAME_DURATION;
|
||||
for (Action action : Action.values()) {
|
||||
frameDurations.put(action, DEFAULT_FRAME_DURATION);
|
||||
}
|
||||
|
||||
hitbox = new Rectangle(0, 0, 64, 128);
|
||||
attackbox = new Rectangle(0, 0, 80, 80);
|
||||
|
||||
speed = 300f;
|
||||
maxHealth = 100;
|
||||
health = maxHealth;
|
||||
attackPower = 10;
|
||||
|
||||
isFacingRight = true;
|
||||
currentAction = Action.IDLE;
|
||||
stateTime = 0;
|
||||
isAnimationFinished = false;
|
||||
|
||||
loadAnimations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载角色动画,由子类实现具体的动画帧加载
|
||||
*/
|
||||
protected abstract void loadAnimations();
|
||||
|
||||
/**
|
||||
* 从精灵图表加载动画
|
||||
*
|
||||
* @param action 动作类型
|
||||
* @param regionPrefix 该动作在图表中的区域前缀
|
||||
* @param frameCount 帧数
|
||||
* @param loop 是否循环
|
||||
*/
|
||||
protected void loadAnimationFromAtlas(Action action, String regionPrefix,
|
||||
int frameCount, boolean loop) {
|
||||
if (atlas == null) {
|
||||
protected void loadAnimationFromAtlas(Action action, String regionPrefix, int frameCount, boolean loop) {
|
||||
if (atlas == null)
|
||||
throw new IllegalStateException("TextureAtlas 未初始化!");
|
||||
}
|
||||
if (frameCount <= 0) {
|
||||
if (frameCount <= 0)
|
||||
throw new IllegalArgumentException("帧数必须大于0: " + frameCount);
|
||||
}
|
||||
|
||||
if (frameDurations == null || frameDurations.length <= action.ordinal()) {
|
||||
throw new IllegalStateException("frameDurations 未初始化或大小不足");
|
||||
}
|
||||
|
||||
Array<TextureRegion> frames = new Array<>();
|
||||
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
String formattedIndex = SimpleFormatter.addLeadingZeros(i, 3);
|
||||
String regionName = regionPrefix + formattedIndex;
|
||||
|
||||
String regionName = regionPrefix + SimpleFormatter.addLeadingZeros(i, 3);
|
||||
TextureRegion region = atlas.findRegion(regionName);
|
||||
|
||||
if (region == null) {
|
||||
throw new IllegalArgumentException("精灵图表中未找到区域: " + regionName +
|
||||
" (前缀: " + regionPrefix + ", 索引: " + i + ")");
|
||||
throw new IllegalArgumentException("未找到区域: " + regionName);
|
||||
}
|
||||
|
||||
frames.add(region);
|
||||
}
|
||||
|
||||
Animation<TextureRegion> animation = new Animation<>(
|
||||
frameDurations[action.ordinal()],
|
||||
frames);
|
||||
Animation<TextureRegion> animation = new Animation<>(frameDurations.get(action), frames);
|
||||
animation.setPlayMode(loop ? Animation.PlayMode.LOOP : Animation.PlayMode.NORMAL);
|
||||
animations[action.ordinal()] = animation;
|
||||
animations.put(action, animation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为特定动作设置帧间隔
|
||||
*/
|
||||
protected void setFrameDuration(Action action, float duration) {
|
||||
frameDurations[action.ordinal()] = duration;
|
||||
|
||||
if (animations[action.ordinal()] != null) {
|
||||
animations[action.ordinal()].setFrameDuration(duration);
|
||||
}
|
||||
frameDurations.put(action, duration);
|
||||
Animation<TextureRegion> anim = animations.get(action);
|
||||
if (anim != null)
|
||||
anim.setFrameDuration(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色状态
|
||||
*/
|
||||
public void update(float deltaTime) {
|
||||
stateTime += deltaTime;
|
||||
isAnimationFinished = animations[currentAction.ordinal()].isAnimationFinished(stateTime);
|
||||
|
||||
Animation<TextureRegion> anim = animations.get(currentAction);
|
||||
if (anim != null) {
|
||||
isAnimationFinished = anim.isAnimationFinished(stateTime);
|
||||
}
|
||||
handleAnimationTransitions();
|
||||
|
||||
updateHitboxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理动画完成后的状态转换,子类可以重写以实现特定逻辑
|
||||
*/
|
||||
protected void handleAnimationTransitions() {
|
||||
Animation<TextureRegion> currentAnim = animations[currentAction.ordinal()];
|
||||
if (!isAnimationFinished)
|
||||
return;
|
||||
|
||||
if (currentAnim.getPlayMode() != Animation.PlayMode.LOOP && isAnimationFinished) {
|
||||
switch (currentAction) {
|
||||
case ATTACK1:
|
||||
case ATTACK2:
|
||||
case ATTACK3:
|
||||
case SPECIAL1:
|
||||
case SPECIAL2:
|
||||
changeAction(Action.IDLE);
|
||||
break;
|
||||
case HIT:
|
||||
changeAction(Action.IDLE);
|
||||
break;
|
||||
case JUMP:
|
||||
changeAction(Action.FALL);
|
||||
break;
|
||||
}
|
||||
switch (currentAction) {
|
||||
case ATTACK1:
|
||||
case ATTACK2:
|
||||
case ATTACK3:
|
||||
case SPECIAL1:
|
||||
case SPECIAL2:
|
||||
case HIT:
|
||||
changeAction(Action.IDLE);
|
||||
break;
|
||||
case JUMP:
|
||||
changeAction(Action.FALL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制角色
|
||||
*/
|
||||
public void render(SpriteBatch batch) {
|
||||
Animation<TextureRegion> currentAnimation = animations[currentAction.ordinal()];
|
||||
if (currentAnimation == null) {
|
||||
Animation<TextureRegion> anim = animations.get(currentAction);
|
||||
if (anim == null) {
|
||||
Gdx.app.error("Fighter", "动画未初始化: " + currentAction);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean loop = currentAnimation.getPlayMode() == Animation.PlayMode.LOOP;
|
||||
TextureRegion currentFrame = currentAnimation.getKeyFrame(stateTime, loop);
|
||||
|
||||
if (currentFrame == null) {
|
||||
TextureRegion frame = anim.getKeyFrame(stateTime, anim.getPlayMode() == Animation.PlayMode.LOOP);
|
||||
if (frame == null) {
|
||||
Gdx.app.error("Fighter", "动画帧为空: " + currentAction);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 计算缩放后的帧尺寸(保持原始比例)
|
||||
float frameWidth = currentFrame.getRegionWidth() * scaleX;
|
||||
float frameHeight = currentFrame.getRegionHeight() * scaleY;
|
||||
float frameWidth = frame.getRegionWidth() * scaleX;
|
||||
float frameHeight = frame.getRegionHeight() * scaleY;
|
||||
float drawX = hitbox.x + (hitbox.width - frameWidth) / 2;
|
||||
float drawY = hitbox.y;
|
||||
|
||||
// 2. 计算绘制位置:始终以hitbox为基准,水平居中、底部对齐(关键!)
|
||||
// 无论是否翻转,x/y坐标都基于hitbox计算,保证位置锚点一致
|
||||
float drawX = hitbox.x + (hitbox.width - frameWidth) / 2; // 水平居中(hitbox中心和帧中心对齐)
|
||||
float drawY = hitbox.y; // 底部对齐(hitbox底部和帧底部对齐)
|
||||
boolean wasFlippedX = frame.isFlipX();
|
||||
frame.flip(!isFacingRight && !wasFlippedX, false);
|
||||
frame.flip(isFacingRight && wasFlippedX, false);
|
||||
|
||||
// 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);
|
||||
batch.draw(frame, drawX, drawY, frameWidth / 2, frameHeight / 2, frameWidth, frameHeight, 1f, 1f, 0f);
|
||||
frame.flip(wasFlippedX != frame.isFlipX(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变角色动作
|
||||
*/
|
||||
public boolean changeAction(Action newAction) {
|
||||
if (isActionUninterruptible(currentAction)) {
|
||||
if (isActionUninterruptible(currentAction))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentAction != newAction) {
|
||||
currentAction = newAction;
|
||||
stateTime = 0;
|
||||
stateTime = 0f;
|
||||
isAnimationFinished = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断动作是否不可被打断
|
||||
*/
|
||||
protected boolean isActionUninterruptible(Action action) {
|
||||
return action == Action.HIT || action == Action.DEATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新碰撞框位置
|
||||
*/
|
||||
protected void updateHitboxes() {
|
||||
if (isFacingRight) {
|
||||
attackbox.setPosition(hitbox.x + hitbox.width - 10, hitbox.y + 20);
|
||||
} else {
|
||||
attackbox.setPosition(hitbox.x - attackbox.width + 10, hitbox.y + 20);
|
||||
}
|
||||
float offsetX = isFacingRight ? hitbox.width - 10 : -attackbox.width + 10;
|
||||
attackbox.setPosition(hitbox.x + offsetX, hitbox.y + 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理角色移动
|
||||
*/
|
||||
public void move(float x, float deltaTime) {
|
||||
if (x != 0) {
|
||||
isFacingRight = x > 0;
|
||||
|
||||
hitbox.x += x * speed * deltaTime;
|
||||
|
||||
handleMoveState();
|
||||
} else if (currentAction == Action.WALK) {
|
||||
changeAction(Action.IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理移动状态转换,子类可重写
|
||||
*/
|
||||
protected void handleMoveState() {
|
||||
if (currentAction != Action.ATTACK1 && currentAction != Action.ATTACK2 &&
|
||||
currentAction != Action.ATTACK3 && currentAction != Action.SPECIAL1 &&
|
||||
currentAction != Action.SPECIAL2 && currentAction != Action.JUMP &&
|
||||
currentAction != Action.FALL && currentAction != Action.DEFEND) {
|
||||
if (!isActionUninterruptible(currentAction) &&
|
||||
currentAction != Action.JUMP &&
|
||||
currentAction != Action.FALL &&
|
||||
currentAction != Action.DEFEND &&
|
||||
!currentAction.name().startsWith("ATTACK") &&
|
||||
!currentAction.name().startsWith("SPECIAL")) {
|
||||
changeAction(Action.WALK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行攻击
|
||||
*/
|
||||
public boolean attack(int attackType) {
|
||||
if (!canAttack()) {
|
||||
if (!canAttack())
|
||||
return false;
|
||||
}
|
||||
|
||||
Action attackAction;
|
||||
switch (attackType) {
|
||||
case 1:
|
||||
attackAction = Action.ATTACK1;
|
||||
break;
|
||||
case 2:
|
||||
attackAction = Action.ATTACK2;
|
||||
break;
|
||||
case 3:
|
||||
attackAction = Action.ATTACK3;
|
||||
break;
|
||||
case 4:
|
||||
attackAction = Action.SPECIAL1;
|
||||
break;
|
||||
case 5:
|
||||
attackAction = Action.SPECIAL2;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Action attackAction = switch (attackType) {
|
||||
case 1 -> Action.ATTACK1;
|
||||
case 2 -> Action.ATTACK2;
|
||||
case 3 -> Action.ATTACK3;
|
||||
case 4 -> Action.SPECIAL1;
|
||||
case 5 -> Action.SPECIAL2;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
return changeAction(attackAction);
|
||||
return attackAction != null && changeAction(attackAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否可以攻击,子类可重写以实现特定逻辑
|
||||
*/
|
||||
protected boolean canAttack() {
|
||||
return currentAction == Action.IDLE || currentAction == Action.WALK;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理受击
|
||||
*/
|
||||
public void takeHit(int damage) {
|
||||
if (currentAction != Action.DEATH) {
|
||||
health -= damage;
|
||||
if (health <= 0) {
|
||||
health = 0;
|
||||
changeAction(Action.DEATH);
|
||||
} else {
|
||||
changeAction(Action.HIT);
|
||||
}
|
||||
}
|
||||
if (currentAction == Action.DEATH)
|
||||
return;
|
||||
|
||||
health = Math.max(0, health - damage);
|
||||
changeAction(health == 0 ? Action.DEATH : Action.HIT);
|
||||
}
|
||||
|
||||
public Rectangle getHitbox() {
|
||||
@@ -362,21 +235,29 @@ public abstract class Fighter implements Disposable {
|
||||
return health;
|
||||
}
|
||||
|
||||
public int getMaxHealth() {
|
||||
return maxHealth;
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (atlas != null)
|
||||
atlas.dispose();
|
||||
}
|
||||
|
||||
public Action getCurrentAction() {
|
||||
return currentAction;
|
||||
}
|
||||
|
||||
public int getAttackPower() {
|
||||
return attackPower;
|
||||
public float getX() {
|
||||
return hitbox.x;
|
||||
}
|
||||
|
||||
public String getName(){ return ""; }
|
||||
public float getY() {
|
||||
return hitbox.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
public float getCenterX() {
|
||||
return hitbox.x + hitbox.width / 2;
|
||||
}
|
||||
|
||||
public float getCenterY() {
|
||||
return hitbox.y + hitbox.height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
70
src/main/java/uno/mloluyu/characters/Reimu.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package uno.mloluyu.characters;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
|
||||
public class Reimu extends Fighter {
|
||||
public Reimu() {
|
||||
super(new TextureAtlas(Gdx.files.internal("src\\main\\resources\\character\\reimu\\reimu.atlas")));
|
||||
|
||||
// 设置角色属性
|
||||
speed = 350f; // 更快的移动速度
|
||||
maxHealth = 90; // 较低的生命值
|
||||
health = maxHealth;
|
||||
attackPower = 12; // 中等攻击力
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
// 加载攻击动作动画
|
||||
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);
|
||||
|
||||
// 加载受击动画
|
||||
loadAnimationFromAtlas(Action.HIT, "hitSpin/hitSpin", 5, false);
|
||||
|
||||
// 设置帧间隔(动作速度)
|
||||
setFrameDuration(Action.IDLE, 0.04f);
|
||||
setFrameDuration(Action.WALK, 0.08f);
|
||||
setFrameDuration(Action.ATTACK1, 0.07f);
|
||||
setFrameDuration(Action.SPECIAL2, 0.06f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoveState() {
|
||||
if (currentAction != Action.ATTACK1 &&
|
||||
currentAction != Action.ATTACK2 &&
|
||||
currentAction != Action.ATTACK3 &&
|
||||
currentAction != Action.SPECIAL1 &&
|
||||
currentAction != Action.SPECIAL2 &&
|
||||
currentAction != Action.DEFEND &&
|
||||
currentAction != Action.JUMP &&
|
||||
currentAction != Action.FALL) {
|
||||
changeAction(Action.WALK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空中也可以攻击
|
||||
*/
|
||||
@Override
|
||||
protected boolean canAttack() {
|
||||
return super.canAttack() || currentAction == Action.JUMP || currentAction == Action.FALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前生命值
|
||||
*/
|
||||
public int getHp() {
|
||||
return health;
|
||||
}
|
||||
}
|
||||
183
src/main/java/uno/mloluyu/desktop/CharacterSelectScreen.java
Normal file
@@ -0,0 +1,183 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
|
||||
import uno.mloluyu.characters.Alice;
|
||||
import uno.mloluyu.characters.Fighter;
|
||||
import uno.mloluyu.characters.Reimu;
|
||||
import uno.mloluyu.util.ClearScreen;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static uno.mloluyu.util.Font.loadChineseFont;
|
||||
|
||||
public class CharacterSelectScreen extends ScreenAdapter {
|
||||
private boolean multiplayerMode = false; // 默认为单人模式
|
||||
|
||||
private final MainGame game;
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
|
||||
private final List<String> characters = Arrays.asList("Alice", "Reimu", "暂定");
|
||||
private int selectedIndex = -1;
|
||||
|
||||
private static final int BUTTON_WIDTH = 300;
|
||||
private static final int BUTTON_HEIGHT = 80;
|
||||
private static final int BUTTON_X = 800;
|
||||
private static final int CONFIRM_Y = 200;
|
||||
private static final int BACK_Y = 100;
|
||||
|
||||
public CharacterSelectScreen(MainGame game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public void setMultiplayerMode(boolean multiplayerMode) {
|
||||
this.multiplayerMode = multiplayerMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
batch = new SpriteBatch();
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
font = loadChineseFont();
|
||||
font.setColor(Color.WHITE);
|
||||
font.getData().setScale(2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
|
||||
renderCharacters(mouseX, mouseY);
|
||||
renderButtons(mouseX, mouseY);
|
||||
renderTexts();
|
||||
|
||||
handleInput(mouseX, mouseY);
|
||||
if (multiplayerMode) {
|
||||
// 显示联机模式提示
|
||||
batch.begin();
|
||||
font.draw(batch, "联机模式 - 等待其他玩家连接...", 100, 100);
|
||||
batch.end();
|
||||
} else {
|
||||
// 显示单人模式提示
|
||||
batch.begin();
|
||||
font.draw(batch, "单人模式", 100, 100);
|
||||
batch.end();
|
||||
}
|
||||
}
|
||||
|
||||
private void renderCharacters(int mouseX, int mouseY) {
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
for (int i = 0; i < characters.size(); i++) {
|
||||
int x = 200;
|
||||
int y = 500 - i * 120;
|
||||
boolean hovered = isHovered(mouseX, mouseY, x, y, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
shapeRenderer.setColor(selectedIndex == i ? Color.GREEN : (hovered ? Color.LIGHT_GRAY : Color.DARK_GRAY));
|
||||
shapeRenderer.rect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
}
|
||||
shapeRenderer.end();
|
||||
}
|
||||
|
||||
private void renderButtons(int mouseX, int mouseY) {
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
drawButton(CONFIRM_Y, mouseX, mouseY, "确认");
|
||||
drawButton(BACK_Y, mouseX, mouseY, "返回");
|
||||
shapeRenderer.end();
|
||||
}
|
||||
|
||||
private void renderTexts() {
|
||||
batch.begin();
|
||||
font.draw(batch, "选择你的角色", 200, 650);
|
||||
for (int i = 0; i < characters.size(); i++) {
|
||||
int x = 200 + 30;
|
||||
int y = 500 - i * 120 + 50;
|
||||
font.draw(batch, characters.get(i), x, y);
|
||||
|
||||
}
|
||||
if (selectedIndex != -1) {
|
||||
font.draw(batch, "已选择: " + characters.get(selectedIndex), 200, 100);
|
||||
}
|
||||
drawButtonText(CONFIRM_Y, "确认");
|
||||
drawButtonText(BACK_Y, "返回");
|
||||
batch.end();
|
||||
}
|
||||
|
||||
private void drawButton(int y, int mouseX, int mouseY, String label) {
|
||||
boolean hovered = isHovered(mouseX, mouseY, BUTTON_X, y, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
shapeRenderer.setColor(hovered ? Color.LIGHT_GRAY : Color.DARK_GRAY);
|
||||
shapeRenderer.rect(BUTTON_X, y, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
}
|
||||
|
||||
private void drawButtonText(int y, String text) {
|
||||
float textX = BUTTON_X + BUTTON_WIDTH / 2f - font.getScaleX() * text.length() * 10;
|
||||
float textY = y + BUTTON_HEIGHT / 2f + 20;
|
||||
font.draw(batch, text, textX, textY);
|
||||
}
|
||||
|
||||
private boolean isHovered(int x, int y, int bx, int by, int bw, int bh) {
|
||||
return x >= bx && x <= bx + bw && y >= by && y <= by + bh;
|
||||
}
|
||||
|
||||
private void handleInput(int mouseX, int mouseY) {
|
||||
if (Gdx.input.justTouched()) {
|
||||
// 检查是否点击了角色按钮
|
||||
for (int i = 0; i < characters.size(); i++) {
|
||||
int x = 200;
|
||||
int y = 500 - i * 120;
|
||||
if (isHovered(mouseX, mouseY, x, y, BUTTON_WIDTH, BUTTON_HEIGHT)) {
|
||||
selectedIndex = i;
|
||||
Gdx.app.log("Character", "选择了角色: " + characters.get(i));
|
||||
return; // 防止同时触发其他按钮
|
||||
}
|
||||
}
|
||||
|
||||
// 点击确认按钮
|
||||
if (isHovered(mouseX, mouseY, BUTTON_X, CONFIRM_Y, BUTTON_WIDTH, BUTTON_HEIGHT)) {
|
||||
if (selectedIndex != -1) {
|
||||
String selectedCharacter = characters.get(selectedIndex);
|
||||
Gdx.app.log("Character", "确认角色: " + selectedCharacter);
|
||||
|
||||
Fighter fighter = null;
|
||||
switch (selectedCharacter) {
|
||||
case "Alice":
|
||||
fighter = new Alice();
|
||||
break;
|
||||
case "Reimu":
|
||||
fighter = new Reimu();
|
||||
break;
|
||||
// case "弓箭手":
|
||||
// fighter = new Archer();
|
||||
// break;
|
||||
}
|
||||
|
||||
if (fighter != null) {
|
||||
game.setScreen(new GameScreen(game, fighter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击返回按钮
|
||||
if (isHovered(mouseX, mouseY, BUTTON_X, BACK_Y, BUTTON_WIDTH, BUTTON_HEIGHT)) {
|
||||
Gdx.app.log("Character", "返回主菜单");
|
||||
game.setScreen(new MainMenuScreen(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
font.dispose();
|
||||
shapeRenderer.dispose();
|
||||
}
|
||||
}
|
||||
23
src/main/java/uno/mloluyu/desktop/DesktopLauncher.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
|
||||
/**
|
||||
* Desktop 平台启动器
|
||||
*/
|
||||
public class DesktopLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
|
||||
float scale = 1.0F;
|
||||
config.width = (int) (1920 * scale); // 窗口宽度
|
||||
config.height = (int) (1080 * scale); // 窗口高度
|
||||
|
||||
config.resizable = false; // 窗口设置为大小不可改变
|
||||
|
||||
new LwjglApplication(new MainGame(), config);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.ApplicationListener;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
import com.badlogic.gdx.utils.Scaling;
|
||||
import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
||||
import com.badlogic.gdx.utils.viewport.ScalingViewport;
|
||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||
import com.badlogic.gdx.utils.viewport.Viewport;
|
||||
import uno.mloluyu.characters.Alice;
|
||||
import uno.mloluyu.characters.FighterList;
|
||||
|
||||
public class GameCore implements ApplicationListener {
|
||||
private SpriteBatch batch;
|
||||
private Viewport viewport;
|
||||
private Gaming gaming;
|
||||
private Texture texture;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
viewport = new ScalingViewport(Scaling.none, Launcher.width, Launcher.width);
|
||||
texture = new Texture(Gdx.files.internal("src\\main\\resources\\backgrounds\\bg.png"));
|
||||
batch = new SpriteBatch();
|
||||
gaming = new Gaming(new Alice(), new Alice());
|
||||
gaming.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClearColor(150, 150, 150, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
viewport.apply();
|
||||
batch.begin();
|
||||
batch.draw(texture, 0, 0);
|
||||
// alice1.update(Gdx.graphics.getDeltaTime());
|
||||
// batch.begin();
|
||||
// alice1.render(batch);
|
||||
// batch.end();
|
||||
gaming.render();
|
||||
batch.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
gaming.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
|
||||
// 应用新的视口设置
|
||||
viewport.update(width, height, true); // 第三个参数 true 表示相机居中
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
}
|
||||
}
|
||||
89
src/main/java/uno/mloluyu/desktop/GameScreen.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import uno.mloluyu.characters.Fighter;
|
||||
import uno.mloluyu.network.NetworkManager;
|
||||
import uno.mloluyu.util.ClearScreen;
|
||||
|
||||
public class GameScreen extends ScreenAdapter {
|
||||
private final MainGame game;
|
||||
private final Fighter player;
|
||||
private SpriteBatch batch;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
|
||||
public GameScreen(MainGame game, Fighter player) {
|
||||
this.game = game;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
batch = new SpriteBatch();
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
|
||||
// 更新角色状态
|
||||
player.update(delta);
|
||||
|
||||
// 发送本机玩家位置
|
||||
if (NetworkManager.getInstance().isConnected()) {
|
||||
NetworkManager.getInstance().sendPosition(player.getX(), player.getY());
|
||||
}
|
||||
|
||||
// 渲染角色
|
||||
batch.begin();
|
||||
player.render(batch);
|
||||
|
||||
// 渲染其他玩家位置(联机模式)
|
||||
if (NetworkManager.getInstance().isConnected()) {
|
||||
renderOtherPlayers();
|
||||
}
|
||||
|
||||
batch.end();
|
||||
}
|
||||
|
||||
private void renderOtherPlayers() {
|
||||
// 获取其他玩家位置并渲染
|
||||
for (String position : NetworkManager.getInstance().getOtherPlayerPositions()) {
|
||||
String[] coords = position.split(",");
|
||||
if (coords.length == 2) {
|
||||
try {
|
||||
float x = Float.parseFloat(coords[0]);
|
||||
float y = Float.parseFloat(coords[1]);
|
||||
|
||||
// 使用形状渲染器绘制其他玩家标记
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
shapeRenderer.setColor(Color.RED);
|
||||
shapeRenderer.circle(x, y, 20); // 红色圆圈表示其他玩家
|
||||
shapeRenderer.end();
|
||||
|
||||
// 在批处理中绘制文字
|
||||
batch.begin();
|
||||
// 这里可以添加玩家名字显示
|
||||
batch.end();
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
Gdx.app.error("GameScreen", "解析玩家位置失败: " + position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
player.dispose();
|
||||
shapeRenderer.dispose();
|
||||
// 断开网络连接
|
||||
NetworkManager.getInstance().disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.ApplicationListener;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
||||
import com.badlogic.gdx.utils.viewport.Viewport;
|
||||
import uno.mloluyu.characters.Fighter;
|
||||
|
||||
public class Gaming {
|
||||
|
||||
private Fighter selfFighter;
|
||||
private Fighter frontFighter;
|
||||
private SpriteBatch batch;
|
||||
|
||||
public Gaming(Fighter selfFighter, Fighter frontFighter) {
|
||||
this.selfFighter = selfFighter;
|
||||
this.frontFighter = frontFighter;
|
||||
}
|
||||
|
||||
public void create() {
|
||||
batch = new SpriteBatch();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
selfFighter.update(Gdx.graphics.getDeltaTime());
|
||||
frontFighter.update(Gdx.graphics.getDeltaTime());
|
||||
batch.begin();
|
||||
selfFighter.render(batch);
|
||||
selfFighter.render(batch);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
selfFighter.dispose();
|
||||
frontFighter.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
|
||||
|
||||
|
||||
public class Launcher {
|
||||
public static final int width = 640;
|
||||
public static final int height = 480;
|
||||
public static void main(String[] args) {
|
||||
Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration();
|
||||
configuration.setTitle("Test Game");
|
||||
configuration.setWindowedMode(width, height);
|
||||
configuration.setForegroundFPS(60);
|
||||
configuration.useVsync(true);
|
||||
new Lwjgl3Application(new GameCore(), configuration);
|
||||
}
|
||||
}
|
||||
|
||||
41
src/main/java/uno/mloluyu/desktop/MainGame.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Game;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
|
||||
public class MainGame extends Game {
|
||||
public static final float WORLD_WIDTH = 1920;
|
||||
public static final float WORLD_HEIGHT = 1080;
|
||||
private StartScreen startScreen;
|
||||
private MainMenuScreen mainMenuScreen;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
startScreen = new StartScreen(this);
|
||||
mainMenuScreen = new MainMenuScreen(this);
|
||||
setScreen(new MainMenuScreen(this));
|
||||
setScreen(startScreen);
|
||||
|
||||
}
|
||||
|
||||
public void showGameScreen() {
|
||||
setScreen(mainMenuScreen);
|
||||
if (startScreen != null) {
|
||||
startScreen.dispose();
|
||||
startScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (startScreen != null) {
|
||||
startScreen.dispose();
|
||||
startScreen = null;
|
||||
}
|
||||
if (mainMenuScreen != null) {
|
||||
mainMenuScreen.dispose();
|
||||
mainMenuScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
103
src/main/java/uno/mloluyu/desktop/MainMenuScreen.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
import uno.mloluyu.characters.Alice;
|
||||
import uno.mloluyu.versatile.FighterController;
|
||||
|
||||
import static uno.mloluyu.util.Font.loadChineseFont;
|
||||
|
||||
public class MainMenuScreen extends ScreenAdapter {
|
||||
|
||||
private final MainGame game;
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
|
||||
// 按钮区域
|
||||
private final int buttonWidth = 400;
|
||||
private final int buttonHeight = 80;
|
||||
private final int buttonX = 760;
|
||||
private final int startY = 600;
|
||||
private final int settingsY = 480;
|
||||
private final int networkY = 360;
|
||||
|
||||
public MainMenuScreen(MainGame game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
batch = new SpriteBatch();
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
font = loadChineseFont();
|
||||
font.setColor(Color.WHITE);
|
||||
font.getData().setScale(2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
Gdx.gl.glClearColor(0.75F, 1, 0.98F, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
|
||||
// 绘制按钮背景
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
drawButtonBox(startY, "开始游戏", mouseX, mouseY);
|
||||
drawButtonBox(settingsY, "设置", mouseX, mouseY);
|
||||
drawButtonBox(networkY, "联网设置", mouseX, mouseY);
|
||||
shapeRenderer.end();
|
||||
|
||||
// 绘制按钮文字
|
||||
batch.begin();
|
||||
drawButtonText(startY, "开始游戏");
|
||||
drawButtonText(settingsY, "设置");
|
||||
drawButtonText(networkY, "联网设置");
|
||||
batch.end();
|
||||
|
||||
// 点击事件
|
||||
if (Gdx.input.justTouched()) {
|
||||
if (isTouched(mouseX, mouseY, buttonX, startY)) {
|
||||
Gdx.app.log("Button", "开始游戏按钮被点击!");
|
||||
game.setScreen(new CharacterSelectScreen(game));
|
||||
} else if (isTouched(mouseX, mouseY, buttonX, settingsY)) {
|
||||
Gdx.app.log("Button", "设置按钮被点击!");
|
||||
} else if (isTouched(mouseX, mouseY, buttonX, networkY)) {
|
||||
Gdx.app.log("Button", "联网设置按钮被点击!");
|
||||
game.setScreen(new NetworkSettingsScreen(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawButtonBox(int y, String label, int mouseX, int mouseY) {
|
||||
boolean hovered = isTouched(mouseX, mouseY, buttonX, y);
|
||||
shapeRenderer.setColor(hovered ? Color.LIGHT_GRAY : Color.DARK_GRAY);
|
||||
shapeRenderer.rect(buttonX, y, buttonWidth, buttonHeight);
|
||||
}
|
||||
|
||||
private void drawButtonText(int y, String text) {
|
||||
float textWidth = font.getRegion().getRegionWidth(); // 粗略估算
|
||||
float textX = buttonX + buttonWidth / 2f - text.length() * 20; // 居中估算
|
||||
float textY = y + buttonHeight / 2f + 20;
|
||||
font.draw(batch, text, textX, textY);
|
||||
}
|
||||
|
||||
private boolean isTouched(int x, int y, int bx, int by) {
|
||||
return x >= bx && x <= bx + buttonWidth && y >= by && y <= by + buttonHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (batch != null) batch.dispose();
|
||||
if (font != null) font.dispose();
|
||||
if (shapeRenderer != null) shapeRenderer.dispose();
|
||||
}
|
||||
}
|
||||
143
src/main/java/uno/mloluyu/desktop/NetworkSettingsScreen.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
import uno.mloluyu.network.ConnectClient;
|
||||
import uno.mloluyu.network.ConnectServer;
|
||||
import static uno.mloluyu.util.Font.loadChineseFont;
|
||||
import uno.mloluyu.util.ClearScreen;
|
||||
|
||||
public class NetworkSettingsScreen extends ScreenAdapter {
|
||||
private final MainGame game;
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
|
||||
private static final int BUTTON_WIDTH = 400;
|
||||
private static final int BUTTON_HEIGHT = 80;
|
||||
private static final int BUTTON_X = 760;
|
||||
private static final int CREATE_ROOM_Y = 500;
|
||||
private static final int JOIN_ROOM_Y = 380;
|
||||
private static final int EXIT_Y = 260; // 退出按钮位置
|
||||
|
||||
public NetworkSettingsScreen(MainGame game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
batch = new SpriteBatch();
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
font = loadChineseFont();
|
||||
font.setColor(Color.WHITE);
|
||||
font.getData().setScale(2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
|
||||
renderButtons(mouseX, mouseY);
|
||||
renderTexts();
|
||||
|
||||
handleInput(mouseX, mouseY);
|
||||
}
|
||||
|
||||
private void renderButtons(int mouseX, int mouseY) {
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
drawButton(CREATE_ROOM_Y, mouseX, mouseY);
|
||||
drawButton(JOIN_ROOM_Y, mouseX, mouseY);
|
||||
drawButton(EXIT_Y, mouseX, mouseY); // 新增退出按钮
|
||||
shapeRenderer.end();
|
||||
}
|
||||
|
||||
private void renderTexts() {
|
||||
batch.begin();
|
||||
font.draw(batch, "联机设置", BUTTON_X + 100, 650);
|
||||
drawButtonText(CREATE_ROOM_Y, "创建房间");
|
||||
drawButtonText(JOIN_ROOM_Y, "加入房间");
|
||||
drawButtonText(EXIT_Y, "返回"); // 新增退出按钮文字
|
||||
batch.end();
|
||||
}
|
||||
|
||||
private void handleInput(int mouseX, int mouseY) {
|
||||
if (Gdx.input.justTouched()) {
|
||||
if (isHovered(mouseX, mouseY, BUTTON_X, CREATE_ROOM_Y)) {
|
||||
Gdx.app.log("Network", "创建房间按钮被点击!");
|
||||
new Thread(new ConnectServer(11455)).start();
|
||||
|
||||
// 添加服务器启动提示信息
|
||||
Gdx.app.log("Network", "服务器已启动,等待玩家连接...");
|
||||
System.out.println("服务器已启动,等待玩家连接...");
|
||||
|
||||
// 创建联机模式标识并传递到角色选择界面
|
||||
CharacterSelectScreen characterSelectScreen = new CharacterSelectScreen(game);
|
||||
characterSelectScreen.setMultiplayerMode(true); // 设置为联机模式
|
||||
game.setScreen(characterSelectScreen);
|
||||
|
||||
} else if (isHovered(mouseX, mouseY, BUTTON_X, JOIN_ROOM_Y)) {
|
||||
Gdx.app.log("Network", "加入房间按钮被点击!");
|
||||
// 使用LibGDX的输入对话框避免AWT线程问题
|
||||
Gdx.input.getTextInput(new com.badlogic.gdx.Input.TextInputListener() {
|
||||
@Override
|
||||
public void input(String ip) {
|
||||
if (ip != null && !ip.trim().isEmpty()) {
|
||||
new Thread(() -> new ConnectClient(ip.trim(), 11455)).start();
|
||||
Gdx.app.log("Network", "正在连接到服务器 " + ip.trim() + "...");
|
||||
System.out.println("正在连接到服务器 " + ip.trim() + "...");
|
||||
|
||||
// 使用postRunnable确保在主线程中执行屏幕切换
|
||||
Gdx.app.postRunnable(() -> {
|
||||
CharacterSelectScreen characterSelectScreen = new CharacterSelectScreen(game);
|
||||
characterSelectScreen.setMultiplayerMode(true);
|
||||
game.setScreen(characterSelectScreen);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void canceled() {
|
||||
Gdx.app.log("Network", "用户取消输入 IP");
|
||||
}
|
||||
}, "请输入服务器 IP 地址", "", "加入房间");
|
||||
|
||||
} else if (isHovered(mouseX, mouseY, BUTTON_X, EXIT_Y)) {
|
||||
Gdx.app.log("Network", "退出按钮被点击!");
|
||||
game.setScreen(new MainMenuScreen(game)); // 或者 Gdx.app.exit(); 直接退出游戏
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawButton(int y, int mouseX, int mouseY) {
|
||||
boolean hovered = isHovered(mouseX, mouseY, BUTTON_X, y);
|
||||
shapeRenderer.setColor(hovered ? Color.LIGHT_GRAY : Color.DARK_GRAY);
|
||||
shapeRenderer.rect(BUTTON_X, y, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
}
|
||||
|
||||
private void drawButtonText(int y, String text) {
|
||||
float textX = BUTTON_X + BUTTON_WIDTH / 2f - font.getScaleX() * text.length() * 10;
|
||||
float textY = y + BUTTON_HEIGHT / 2f + 20;
|
||||
font.draw(batch, text, textX, textY);
|
||||
}
|
||||
|
||||
private boolean isHovered(int x, int y, int bx, int by) {
|
||||
return x >= bx && x <= bx + BUTTON_WIDTH && y >= by && y <= by + BUTTON_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
font.dispose();
|
||||
shapeRenderer.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
80
src/main/java/uno/mloluyu/desktop/StartScreen.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Screen;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
|
||||
/**
|
||||
* 启动屏幕类
|
||||
* 显示游戏Logo并在3秒后切换到主菜单界面
|
||||
*/
|
||||
public class StartScreen implements Screen {
|
||||
|
||||
private MainGame mainGame;
|
||||
|
||||
private Texture logoTexture;
|
||||
private com.badlogic.gdx.graphics.g2d.SpriteBatch batch;
|
||||
|
||||
private float deltaSum;
|
||||
|
||||
public StartScreen(MainGame mainGame) {
|
||||
this.mainGame = mainGame;
|
||||
logoTexture = new Texture(Gdx.files.internal("src\\main\\resources\\logo.png"));
|
||||
batch = new com.badlogic.gdx.graphics.g2d.SpriteBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
deltaSum = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
deltaSum += delta;
|
||||
|
||||
if (deltaSum >= 3.0F) {
|
||||
if (mainGame != null) {
|
||||
mainGame.showGameScreen();
|
||||
System.out.println("已经切换到主菜单");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Gdx.gl.glClearColor(0.75F, 1, 0.98F, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
batch.begin();
|
||||
float x = (MainGame.WORLD_WIDTH - logoTexture.getWidth()) / 2f;
|
||||
float y = (MainGame.WORLD_HEIGHT - logoTexture.getHeight()) / 2f;
|
||||
batch.draw(logoTexture, x, y);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (batch != null) {
|
||||
batch.dispose();
|
||||
}
|
||||
if (logoTexture != null) {
|
||||
logoTexture.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
122
src/main/java/uno/mloluyu/desktop/TransitionScreen.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package uno.mloluyu.desktop;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Screen;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
|
||||
/**
|
||||
* 屏幕过渡效果类
|
||||
* 用于在不同屏幕之间实现平滑的过渡动画
|
||||
*/
|
||||
public class TransitionScreen implements Screen {
|
||||
private ShapeRenderer shapeRenderer;
|
||||
private SpriteBatch batch;
|
||||
private float transitionTime = 0;
|
||||
private float totalTransitionTime = 0.5f; // 过渡时间为0.5秒
|
||||
private Runnable targetScreenAction;
|
||||
private TransitionType transitionType = TransitionType.FADE_OUT_FADE_IN;
|
||||
|
||||
/**
|
||||
* 过渡类型枚举
|
||||
*/
|
||||
public enum TransitionType {
|
||||
FADE_OUT_FADE_IN
|
||||
}
|
||||
|
||||
public TransitionScreen() {
|
||||
this.shapeRenderer = new ShapeRenderer();
|
||||
this.batch = new SpriteBatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置目标屏幕的显示动作
|
||||
*
|
||||
* @param targetScreenAction 目标屏幕的显示动作
|
||||
*/
|
||||
public void setTargetScreen(Runnable targetScreenAction) {
|
||||
this.targetScreenAction = targetScreenAction;
|
||||
this.transitionTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
// 屏幕显示时的初始化操作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
// 清屏
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
transitionTime += delta;
|
||||
float progress = Math.min(transitionTime / totalTransitionTime, 1);
|
||||
|
||||
// 绘制过渡效果
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
shapeRenderer.setColor(0, 0, 0, getAlpha(progress));
|
||||
shapeRenderer.rect(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
shapeRenderer.end();
|
||||
|
||||
// 当过渡动画完成时,执行目标屏幕的显示动作
|
||||
if (progress >= 1 && targetScreenAction != null) {
|
||||
targetScreenAction.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过渡进度计算透明度
|
||||
*
|
||||
* @param progress 过渡进度(0-1)
|
||||
* @return 透明度值(0-1)
|
||||
*/
|
||||
private float getAlpha(float progress) {
|
||||
switch (transitionType) {
|
||||
case FADE_OUT_FADE_IN:
|
||||
// 前半段淡出(透明度从0到1),后半段淡入(透明度从1到0)
|
||||
if (progress < 0.5) {
|
||||
return progress * 2;
|
||||
} else {
|
||||
return (1 - progress) * 2;
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
// 屏幕尺寸变化时的处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
// 游戏暂停时的处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
// 游戏恢复时的处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
// 屏幕隐藏时的处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// 释放资源
|
||||
if (shapeRenderer != null) {
|
||||
shapeRenderer.dispose();
|
||||
shapeRenderer = null;
|
||||
}
|
||||
|
||||
if (batch != null) {
|
||||
batch.dispose();
|
||||
batch = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/main/java/uno/mloluyu/network/ConnectClient.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package uno.mloluyu.network;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端连接类(实例方式)
|
||||
*/
|
||||
public class ConnectClient {
|
||||
private Socket socket;
|
||||
|
||||
public ConnectClient(String ip, int port) {
|
||||
try {
|
||||
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, ip, port, null);
|
||||
Gdx.app.log("Client", "成功连接到服务器: " + ip + ":" + port);
|
||||
|
||||
// 启动接收线程
|
||||
new Thread(this::receiveMessages).start();
|
||||
|
||||
// 示例:发送一条欢迎消息
|
||||
sendMessage("你好,我是客户端玩家");
|
||||
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Client", "连接失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ... 现有代码 ...
|
||||
|
||||
private void receiveMessages() {
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
while (true) {
|
||||
int read = socket.getInputStream().read(buffer);
|
||||
if (read == -1) break;
|
||||
String message = new String(buffer, 0, read, StandardCharsets.UTF_8);
|
||||
Gdx.app.log("Client", "收到服务器消息: " + message);
|
||||
|
||||
// 处理位置消息
|
||||
if (message.startsWith("POS:")) {
|
||||
String positionData = message.substring(4);
|
||||
// 通知网络管理器更新其他玩家位置
|
||||
NetworkManager.getInstance().updatePlayerPosition(positionData);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Client", "接收消息异常: " + e.getMessage(), e);
|
||||
} finally {
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// ... 现有代码 ...
|
||||
|
||||
|
||||
public void sendMessage(String message) {
|
||||
try {
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(message.getBytes(StandardCharsets.UTF_8));
|
||||
out.flush();
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Client", "发送消息失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (socket != null) {
|
||||
socket.dispose();
|
||||
socket = null;
|
||||
Gdx.app.log("Client", "已断开与服务器的连接");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,44 +2,161 @@ package uno.mloluyu.network;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.net.ServerSocket;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
import uno.mloluyu.desktop.Gaming;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConnectServer {
|
||||
private static Socket socket;
|
||||
private static String host = "";
|
||||
private static int port = 10800;
|
||||
/**
|
||||
* 支持两个玩家连接的服务器类
|
||||
*/
|
||||
public class ConnectServer implements Runnable {
|
||||
private final int port;
|
||||
private ServerSocket serverSocket;
|
||||
private final List<Player> connectedPlayers = new ArrayList<>();
|
||||
private static final int MAX_PLAYERS = 2;
|
||||
|
||||
public static void connectServer() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, host, port, null);
|
||||
OutputStream outputStream = socket.getOutputStream();//读取套接字的数据流
|
||||
InputStream inputStream = socket.getInputStream();
|
||||
//Gaming gaming = new Gaming(); //进入游戏界面
|
||||
//gaming.render();
|
||||
// 玩家内部类,存储Socket和玩家名字
|
||||
private static class Player {
|
||||
Socket socket;
|
||||
String playerName;
|
||||
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Player(Socket socket) {
|
||||
this.socket = socket;
|
||||
this.playerName = "玩家" + (int) (Math.random() * 1000); // 默认随机名字
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
void setPlayerName(String name) {
|
||||
this.playerName = name;
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
socket.dispose();
|
||||
}
|
||||
Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectServer(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
serverSocket = Gdx.net.newServerSocket(Net.Protocol.TCP, port, null);
|
||||
Gdx.app.log("Server", "服务器已启动,等待玩家连接...");
|
||||
|
||||
try {
|
||||
while (connectedPlayers.size() < MAX_PLAYERS) {
|
||||
Socket socket = serverSocket.accept(null);
|
||||
Player player = new Player(socket);
|
||||
connectedPlayers.add(player);
|
||||
Gdx.app.log("Server", "玩家连接成功: " + socket.getRemoteAddress() + ",默认名字: " + player.getPlayerName());
|
||||
new Thread(() -> handlePlayer(player)).start();
|
||||
}
|
||||
|
||||
Gdx.app.log("Server", "已连接两个玩家,游戏准备开始!");
|
||||
// 输出当前所有玩家名字
|
||||
for (int i = 0; i < connectedPlayers.size(); i++) {
|
||||
Gdx.app.log("Server", "玩家" + (i + 1) + ": " + connectedPlayers.get(i).getPlayerName());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Server", "连接异常: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ... 现有代码 ...
|
||||
|
||||
private void handlePlayer(Player player) {
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
while (true) {
|
||||
int read = player.getSocket().getInputStream().read(buffer);
|
||||
if (read == -1)
|
||||
break;
|
||||
String message = new String(buffer, 0, read);
|
||||
Gdx.app.log("Server", "收到玩家" + player.getPlayerName() + "的消息: " + message);
|
||||
|
||||
// 处理设置玩家名字的消息
|
||||
if (message.startsWith("SET_NAME:")) {
|
||||
String newName = message.substring(8);
|
||||
player.setPlayerName(newName);
|
||||
Gdx.app.log("Server", "玩家名字已更新为: " + newName);
|
||||
}
|
||||
// 处理位置消息并广播
|
||||
else if (message.startsWith("POS:")) {
|
||||
String positionData = message.substring(4);
|
||||
// 广播给其他玩家
|
||||
broadcastToOtherPlayers(player, "POS:" + positionData);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Server", "玩家通信异常: " + e.getMessage(), e);
|
||||
} finally {
|
||||
player.getSocket().dispose();
|
||||
connectedPlayers.remove(player);
|
||||
Gdx.app.log("Server", "玩家" + player.getPlayerName() + "断开连接");
|
||||
}
|
||||
}
|
||||
|
||||
// 新增广播方法
|
||||
private void broadcastToOtherPlayers(Player sender, String message) {
|
||||
for (Player player : connectedPlayers) {
|
||||
if (player != sender) {
|
||||
try {
|
||||
OutputStream out = player.getSocket().getOutputStream();
|
||||
out.write(message.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
out.flush();
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Server", "广播消息失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... 现有代码 ...
|
||||
|
||||
// 获取当前服务器所有玩家名字的方法
|
||||
public List<String> getPlayerNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
for (Player player : connectedPlayers) {
|
||||
names.add(player.getPlayerName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
// 根据索引获取特定玩家名字的方法
|
||||
public String getPlayerName(int index) {
|
||||
if (index >= 0 && index < connectedPlayers.size()) {
|
||||
return connectedPlayers.get(index).getPlayerName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 设置玩家名字的方法
|
||||
public void setPlayerName(int index, String name) {
|
||||
if (index >= 0 && index < connectedPlayers.size()) {
|
||||
connectedPlayers.get(index).setPlayerName(name);
|
||||
Gdx.app.log("Server", "玩家" + index + "名字设置为: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (Player player : connectedPlayers) {
|
||||
player.getSocket().dispose();
|
||||
}
|
||||
connectedPlayers.clear();
|
||||
|
||||
if (serverSocket != null) {
|
||||
serverSocket.dispose();
|
||||
serverSocket = null;
|
||||
}
|
||||
|
||||
Gdx.app.log("Server", "服务器已关闭");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package uno.mloluyu.network;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.net.ServerSocket;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
|
||||
/**
|
||||
* @author mloluyu
|
||||
*/
|
||||
public class CreateServer {
|
||||
//创建套接字
|
||||
private static ServerSocket serverSocket;
|
||||
private static Socket clientSocket;
|
||||
|
||||
public static void createServer(int port) {
|
||||
//开启本地服务器
|
||||
serverSocket = Gdx.net.newServerSocket(Net.Protocol.TCP, port, null);
|
||||
|
||||
//启用新线程处理
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
clientSocket = serverSocket.accept(null);
|
||||
System.out.println("玩家连接"+clientSocket);
|
||||
//连接上后在这里编写进入游戏界面的代码
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
if (clientSocket != null) {
|
||||
clientSocket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void serverDispose() {
|
||||
//关闭主机
|
||||
if (serverSocket != null) {
|
||||
serverSocket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/main/java/uno/mloluyu/network/NetworkManager.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package uno.mloluyu.network;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 网络管理器,协调服务器和客户端通信
|
||||
*/
|
||||
public class NetworkManager {
|
||||
private static NetworkManager instance;
|
||||
private ConnectServer server;
|
||||
private ConnectClient client;
|
||||
private boolean isHost = false;
|
||||
private List<String> playerPositions = new ArrayList<>();
|
||||
|
||||
public static NetworkManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new NetworkManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建房间(作为主机)
|
||||
*/
|
||||
public void createRoom() {
|
||||
isHost = true;
|
||||
server = new ConnectServer(11455);
|
||||
new Thread(server).start();
|
||||
Gdx.app.log("Network", "房间创建成功,等待其他玩家加入...");
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入房间(作为客户端)
|
||||
*/
|
||||
public void joinRoom(String ip) {
|
||||
isHost = false;
|
||||
client = new ConnectClient(ip, 11455);
|
||||
Gdx.app.log("Network", "正在加入房间: " + ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送玩家位置信息
|
||||
*/
|
||||
public void sendPosition(float x, float y) {
|
||||
if (isHost && server != null) {
|
||||
// 主机直接广播位置
|
||||
broadcastMessage("POS:" + x + "," + y);
|
||||
} else if (client != null) {
|
||||
// 客户端发送位置到服务器
|
||||
client.sendMessage("POS:" + x + "," + y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播消息(主机使用)
|
||||
*/
|
||||
private void broadcastMessage(String message) {
|
||||
// 这里需要实现广播逻辑
|
||||
Gdx.app.log("Network", "广播消息: " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取其他玩家位置
|
||||
*/
|
||||
public List<String> getOtherPlayerPositions() {
|
||||
return playerPositions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (server != null) {
|
||||
server.dispose();
|
||||
server = null;
|
||||
}
|
||||
if (client != null) {
|
||||
client.disconnect();
|
||||
client = null;
|
||||
}
|
||||
playerPositions.clear();
|
||||
Gdx.app.log("Network", "网络连接已断开");
|
||||
}
|
||||
|
||||
public boolean isHost() {
|
||||
return isHost;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return server != null || client != null;
|
||||
}
|
||||
|
||||
public void updatePlayerPosition(String positionData) {
|
||||
// 简单实现:直接存储位置数据
|
||||
playerPositions.add(positionData);
|
||||
|
||||
// 限制位置列表大小,避免内存泄漏
|
||||
if (playerPositions.size() > 10) {
|
||||
playerPositions.remove(0);
|
||||
}
|
||||
|
||||
Gdx.app.log("Network", "更新其他玩家位置: " + positionData);
|
||||
}
|
||||
}
|
||||
11
src/main/java/uno/mloluyu/util/ClearScreen.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package uno.mloluyu.util;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class ClearScreen {
|
||||
public ClearScreen() {
|
||||
Gdx.gl.glClearColor(0.3F, 0.3F, 0.5F, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
31
src/main/java/uno/mloluyu/util/Font.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package uno.mloluyu.util;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
|
||||
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
|
||||
|
||||
public class Font {
|
||||
public static BitmapFont loadChineseFont() {
|
||||
FreeTypeFontGenerator generator = null;
|
||||
try {
|
||||
generator = new FreeTypeFontGenerator(Gdx.files.internal("FLyouzichati-Regular-2.ttf")); // 你的中文字体路径
|
||||
FreeTypeFontParameter parameter = new FreeTypeFontParameter();
|
||||
parameter.size = 48;
|
||||
parameter.color = Color.WHITE;
|
||||
parameter.borderWidth = 1;
|
||||
parameter.borderColor = Color.DARK_GRAY;
|
||||
parameter.characters = "返回主菜单确认角色选择了角色人游戏加入联机模式 - 等待其他玩家连接...房间创建房间联机设置开始游戏设置联网中国abcdefghijklmnopqrstuvw暂定xyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
return generator.generateFont(parameter);
|
||||
} catch (Exception e) {
|
||||
Gdx.app.error("Font Error", "加载中文字体失败: " + e.getMessage());
|
||||
return new BitmapFont(); // 回退默认字体
|
||||
} finally {
|
||||
if (generator != null)
|
||||
generator.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
src/main/java/uno/mloluyu/versatile/ButtonActions.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package uno.mloluyu.versatile;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
|
||||
/**
|
||||
* 按钮资源管理类:统一管理按钮枚举和图集资源
|
||||
*/
|
||||
public class ButtonActions {
|
||||
|
||||
private static TextureAtlas atlas;
|
||||
|
||||
// 初始化图集(建议在游戏启动或Screen加载时调用一次)
|
||||
public static void loadAtlas(String atlasPath) {
|
||||
if (atlas == null) {
|
||||
atlas = new TextureAtlas(Gdx.files.internal(atlasPath));
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图集区域
|
||||
public static TextureRegion getRegion(Button button) {
|
||||
if (atlas == null) {
|
||||
throw new IllegalStateException("按钮图集尚未加载,请先调用 ButtonActions.loadAtlas()");
|
||||
}
|
||||
TextureRegion region = atlas.findRegion(button.getRegionName());
|
||||
if (region == null) {
|
||||
throw new IllegalArgumentException("图集中未找到区域: " + button.getRegionName());
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
// 释放资源(建议在Screen dispose时调用)
|
||||
public static void dispose() {
|
||||
if (atlas != null) {
|
||||
atlas.dispose();
|
||||
atlas = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮枚举:统一定义所有按钮区域名
|
||||
*/
|
||||
public enum Button {
|
||||
CONFIRM("confirm"), // 确认按钮
|
||||
BACK("back"), // 返回按钮
|
||||
WARRIOR("warrior"), // 战士角色按钮
|
||||
MAGE("mage"), // 法师角色按钮
|
||||
ARCHER("archer"); // 弓箭手角色按钮
|
||||
|
||||
private final String regionName;
|
||||
|
||||
Button(String regionName) {
|
||||
this.regionName = regionName;
|
||||
}
|
||||
|
||||
public String getRegionName() {
|
||||
return regionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package uno.mloluyu.Controller;
|
||||
package uno.mloluyu.versatile;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
@@ -21,6 +21,9 @@ public class FighterController extends InputAdapter {
|
||||
public FighterController(Fighter fighter) {
|
||||
this.fighter = fighter;
|
||||
}
|
||||
public FighterController() {
|
||||
this.fighter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新控制器状态和冷却时间
|
||||
@@ -33,6 +36,7 @@ public class FighterController extends InputAdapter {
|
||||
|
||||
// 处理移动输入
|
||||
handleMovement();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
BIN
src/main/resources/FLyouzichati-Regular-2.ttf
Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 3.9 MiB |
BIN
src/main/resources/character/reimu/reimu-0.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
src/main/resources/character/reimu/reimu-1.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
src/main/resources/character/reimu/reimu-2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
2008
src/main/resources/character/reimu/reimu.atlas
Normal file
BIN
src/main/resources/logo.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
24
src/main/resources/ui/uiskin.atlas
Normal file
@@ -0,0 +1,24 @@
|
||||
uiskin.png
|
||||
format: RGBA8888
|
||||
filter: Nearest,Nearest
|
||||
repeat: none
|
||||
button-up
|
||||
rotate: false
|
||||
xy: 0, 0
|
||||
size: 200, 60
|
||||
split: 10, 10, 10, 10
|
||||
orig: 200, 60
|
||||
offset: 0, 0
|
||||
button-down
|
||||
rotate: false
|
||||
xy: 0, 60
|
||||
size: 200, 60
|
||||
split: 10, 10, 10, 10
|
||||
orig: 200, 60
|
||||
offset: 0, 0
|
||||
white
|
||||
rotate: false
|
||||
xy: 0, 120
|
||||
size: 1, 1
|
||||
orig: 1, 1
|
||||
offset: 0, 0
|
||||
46
src/main/resources/ui/uiskin.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"com.badlogic.gdx.graphics.g2d.BitmapFont": {
|
||||
"default-font": {
|
||||
"file": "default.fnt"
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.graphics.Color": {
|
||||
"black": {
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 1
|
||||
},
|
||||
"white": {
|
||||
"r": 1,
|
||||
"g": 1,
|
||||
"b": 1,
|
||||
"a": 1
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable": {
|
||||
"dialogDim": {
|
||||
"name": "white",
|
||||
"color": {
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 0.45
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.Button$ButtonStyle": {
|
||||
"default": {
|
||||
"up": "button-up",
|
||||
"down": "button-down"
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle": {
|
||||
"default": {
|
||||
"up": "button-up",
|
||||
"down": "button-down",
|
||||
"font": "default-font",
|
||||
"fontColor": "black"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
target/classes/FLyouzichati-Regular-2.ttf
Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 3.9 MiB |
BIN
target/classes/character/reimu/reimu-0.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
target/classes/character/reimu/reimu-1.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
target/classes/character/reimu/reimu-2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
2008
target/classes/character/reimu/reimu.atlas
Normal file
BIN
target/classes/logo.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
24
target/classes/ui/uiskin.atlas
Normal file
@@ -0,0 +1,24 @@
|
||||
uiskin.png
|
||||
format: RGBA8888
|
||||
filter: Nearest,Nearest
|
||||
repeat: none
|
||||
button-up
|
||||
rotate: false
|
||||
xy: 0, 0
|
||||
size: 200, 60
|
||||
split: 10, 10, 10, 10
|
||||
orig: 200, 60
|
||||
offset: 0, 0
|
||||
button-down
|
||||
rotate: false
|
||||
xy: 0, 60
|
||||
size: 200, 60
|
||||
split: 10, 10, 10, 10
|
||||
orig: 200, 60
|
||||
offset: 0, 0
|
||||
white
|
||||
rotate: false
|
||||
xy: 0, 120
|
||||
size: 1, 1
|
||||
orig: 1, 1
|
||||
offset: 0, 0
|
||||
46
target/classes/ui/uiskin.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"com.badlogic.gdx.graphics.g2d.BitmapFont": {
|
||||
"default-font": {
|
||||
"file": "default.fnt"
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.graphics.Color": {
|
||||
"black": {
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 1
|
||||
},
|
||||
"white": {
|
||||
"r": 1,
|
||||
"g": 1,
|
||||
"b": 1,
|
||||
"a": 1
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable": {
|
||||
"dialogDim": {
|
||||
"name": "white",
|
||||
"color": {
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 0.45
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.Button$ButtonStyle": {
|
||||
"default": {
|
||||
"up": "button-up",
|
||||
"down": "button-down"
|
||||
}
|
||||
},
|
||||
"com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle": {
|
||||
"default": {
|
||||
"up": "button-up",
|
||||
"down": "button-down",
|
||||
"font": "default-font",
|
||||
"fontColor": "black"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
target/classes/uno/mloluyu/characters/FighterList.class
Normal file
BIN
target/classes/uno/mloluyu/characters/Reimu.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/CharacterSelectScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/DesktopLauncher.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/GameScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/MainGame.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/MainMenuScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/NetworkSettingsScreen$1.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/NetworkSettingsScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/StartScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/TransitionScreen.class
Normal file
BIN
target/classes/uno/mloluyu/network/ConnectClient.class
Normal file
BIN
target/classes/uno/mloluyu/network/ConnectServer$Player.class
Normal file
BIN
target/classes/uno/mloluyu/network/ConnectServer.class
Normal file
BIN
target/classes/uno/mloluyu/network/NetworkManager.class
Normal file
BIN
target/classes/uno/mloluyu/util/ClearScreen.class
Normal file
BIN
target/classes/uno/mloluyu/util/Font.class
Normal file
BIN
target/classes/uno/mloluyu/util/SimpleFormatter.class
Normal file
BIN
target/classes/uno/mloluyu/versatile/ButtonActions$Button.class
Normal file
BIN
target/classes/uno/mloluyu/versatile/ButtonActions.class
Normal file
BIN
target/classes/uno/mloluyu/versatile/FighterController.class
Normal file
BIN
target/game-1.0-SNAPSHOT.jar
Normal file
3
target/maven-archiver/pom.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
artifactId=game
|
||||
groupId=uno.mloluyu
|
||||
version=1.0-SNAPSHOT
|
||||
@@ -0,0 +1,13 @@
|
||||
uno\mloluyu\network\ConnectServer$1$1.class
|
||||
uno\mloluyu\network\ConnectServer$1.class
|
||||
uno\mloluyu\network\CreateServer$1.class
|
||||
uno\mloluyu\characters\FighterList.class
|
||||
uno\mloluyu\network\CreateServer.class
|
||||
uno\mloluyu\desktop\Launcher.class
|
||||
uno\mloluyu\characters\Fighter$Action.class
|
||||
uno\mloluyu\desktop\GameCore.class
|
||||
uno\mloluyu\characters\Fighter.class
|
||||
uno\mloluyu\characters\Alice.class
|
||||
uno\mloluyu\Controller\FighterController.class
|
||||
uno\mloluyu\network\ConnectServer.class
|
||||
uno\mloluyu\util\SimpleFormatter.class
|
||||
@@ -0,0 +1,10 @@
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\characters\Alice.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\characters\Fighter.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\characters\FighterList.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\Controller\FighterController.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\desktop\GameCore.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\desktop\Launcher.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\desktop\MainMenuScreen.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\network\ConnectServer.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\network\CreateServer.java
|
||||
C:\Users\www\Documents\Game\Game\src\main\java\uno\mloluyu\util\SimpleFormatter.java
|
||||