优化
This commit is contained in:
12
docs/perf_hotspots.md
Normal file
12
docs/perf_hotspots.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 性能热点初稿
|
||||
|
||||
首轮标记:
|
||||
|
||||
| 区域 | 说明 | 优先级 | 说明 |
|
||||
|------|------|--------|------|
|
||||
GameScreen.render | 多次集合遍历 + 远程玩家循环内更新 | 高 | 后续拆分逻辑/渲染阶段 |
|
||||
SimpleFighter.update | 物理与攻击状态混杂 | 中 | 拆分到组件式(动作/物理) |
|
||||
网络同步(待补) | sendPosition 每帧发送 | 中 | 引入位置压缩/频率限制 |
|
||||
清屏/批处理 | 现已静态化 | 已改善 | 继续合并渲染批次 |
|
||||
|
||||
后续收集: 帧时间分布 / GC 次数。
|
||||
125
pom.xml
125
pom.xml
@@ -14,71 +14,87 @@
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<gdx.version>1.12.1</gdx.version>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx</artifactId>
|
||||
<version>1.12.1</version> <!-- 替换为你的 LibGDX 版本 -->
|
||||
<!-- Core LibGDX -->
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx</artifactId>
|
||||
<version>${gdx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- LWJGL2 Desktop Backend (使用 LwjglApplication 而不是 Lwjgl3Application) -->
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-backend-lwjgl</artifactId>
|
||||
<version>${gdx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 平台原生库(桌面) -->
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-platform</artifactId>
|
||||
<version>${gdx.version}</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</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>
|
||||
<version>1.12.1</version>
|
||||
<artifactId>gdx-freetype</artifactId>
|
||||
<version>${gdx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-backend-lwjgl3</artifactId>
|
||||
<version>1.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-platform</artifactId>
|
||||
<version>1.12.1</version>
|
||||
<artifactId>gdx-freetype-platform</artifactId>
|
||||
<version>${gdx.version}</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</dependency>
|
||||
<!-- JUnit 5 -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Enforcer 防止依赖冲突 / 旧 JDK -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce</id>
|
||||
<goals><goal>enforce</goal></goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireJavaVersion>
|
||||
<version>[21,)</version>
|
||||
</requireJavaVersion>
|
||||
<dependencyConvergence/>
|
||||
</rules>
|
||||
<fail>false</fail>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<!-- 建议使用与LibGDX兼容的JDK版本,11或17比较合适 -->
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<!-- 移除不必要的预览特性,除非确实需要 -->
|
||||
<!-- 与 properties 中保持一致 -->
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
<release>${maven.compiler.release}</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -87,8 +103,6 @@
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<!-- 确保这个类路径与你实际的Launcher类位置一致 -->
|
||||
<!-- 例如:如果你的类文件在src/main/java/uno/mloluyu/Launcher.java -->
|
||||
<mainClass>uno.mloluyu.desktop.DesktopLauncher</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -107,6 +121,31 @@
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 运行 JUnit 5 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 初步覆盖率,仅生成报告,不设阈值 -->
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.11</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals><goal>prepare-agent</goal></goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals><goal>report</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
16
src/main/java/uno/mloluyu/desktop/BaseScreen.java
Normal file
16
src/main/java/uno/mloluyu/desktop/BaseScreen.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
import com.badlogic.gdx.ScreenAdapter;
|
||||
|
||||
/** 所有 Screen 的基础类,集中生命周期钩子扩展。 */
|
||||
public abstract class BaseScreen extends ScreenAdapter {
|
||||
protected final MainGame game;
|
||||
|
||||
protected BaseScreen(MainGame game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
/** 提供可选的资源预加载完成回调 */
|
||||
public void onAssetsReady() {
|
||||
}
|
||||
}
|
||||
@@ -33,14 +33,12 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
private SimpleFighter selectedFighter2;
|
||||
|
||||
private final List<Texture> bgs = Arrays.asList(
|
||||
new Texture(Gdx.files.internal("src/main/resources/selectpage/10b_back_blue2p.png")),
|
||||
//new Texture(Gdx.files.internal("src/main/resources/selectpage/back_door.png")),
|
||||
new Texture(Gdx.files.internal("src/main/resources/selectpage/11b_back_red1p.png"))
|
||||
);
|
||||
new Texture(Gdx.files.internal("selectpage/10b_back_blue2p.png")),
|
||||
// new Texture(Gdx.files.internal("selectpage/back_door.png")),
|
||||
new Texture(Gdx.files.internal("selectpage/11b_back_red1p.png")));
|
||||
private final List<Texture> charsTexts = Arrays.asList(
|
||||
new Texture(Gdx.files.internal("src/main/resources/selectpage/character_03.png")),
|
||||
new Texture(Gdx.files.internal("src/main/resources/selectpage/character_00.png"))
|
||||
);
|
||||
new Texture(Gdx.files.internal("selectpage/character_03.png")),
|
||||
new Texture(Gdx.files.internal("selectpage/character_00.png")));
|
||||
private final List<String> characters = Arrays.asList("Alice", "Reimu", "暂定");
|
||||
private Texture profile1p = charsTexts.get(0);
|
||||
private Texture profile2p = charsTexts.get(1);
|
||||
@@ -49,11 +47,13 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
private static int selectedIndex = 0;
|
||||
private static boolean is1P = true;
|
||||
|
||||
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;
|
||||
// 下面这些按钮常量原本用于文本/按钮绘制,当前 UI 逻辑已注释。
|
||||
// 如果后续恢复 renderTexts() 可重新启用;为减少无用警告暂时注释。
|
||||
// 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;
|
||||
@@ -75,13 +75,14 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
// 清屏:使用工具静态方法,避免误用私有构造器
|
||||
ClearScreen.clear();
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
renderBackground();
|
||||
renderCharacters(multiplayerMode);
|
||||
// renderTexts();
|
||||
// renderTexts();
|
||||
|
||||
handleInput(mouseX, mouseY);
|
||||
if (multiplayerMode) {
|
||||
@@ -99,7 +100,7 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
|
||||
private void renderBackground() {
|
||||
batch.begin();
|
||||
for (int i = 0; i < bgs.size(); i ++) {
|
||||
for (int i = 0; i < bgs.size(); i++) {
|
||||
batch.draw(bgs.get(i), 0, 528 * i, 1920, 528);
|
||||
}
|
||||
batch.end();
|
||||
@@ -107,45 +108,47 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
|
||||
private void renderCharacters(boolean multiplayerMode) {
|
||||
batch.begin();
|
||||
batch.draw(profile1p, 0, 0, profile1p.getWidth()*3, profile1p.getHeight()*3);
|
||||
batch.draw(profile2p, 0, 528, profile2p.getWidth()*3, profile2p.getHeight()*3);
|
||||
batch.draw(profile1p, 0, 0, profile1p.getWidth() * 3, profile1p.getHeight() * 3);
|
||||
batch.draw(profile2p, 0, 528, profile2p.getWidth() * 3, profile2p.getHeight() * 3);
|
||||
batch.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 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 handleInput(int mouseX, int mouseY) {
|
||||
if (selectedFighter1 != null && selectedFighter2 != null) {
|
||||
if (multiplayerMode) {
|
||||
// 设置唯一玩家 ID 并发送角色选择
|
||||
if (NetworkManager.getInstance().getLocalPlayerId() == null) {
|
||||
String playerId = UUID.randomUUID().toString();
|
||||
NetworkManager.getInstance().setLocalPlayerId(playerId);
|
||||
Gdx.app.log("Network", "设置玩家ID: " + playerId);
|
||||
}
|
||||
NetworkManager.getInstance().sendCharacterSelection(selectedFighter1.getName());
|
||||
// 单人模式:只要1P选择了角色就进入游戏
|
||||
if (!multiplayerMode && selectedFighter1 != null) {
|
||||
game.setScreen(new GameScreen(game, selectedFighter1));
|
||||
return;
|
||||
}
|
||||
// 联机模式:等待双方选择(当前逻辑仍采用2人都选才进入)
|
||||
if (multiplayerMode && selectedFighter1 != null && selectedFighter2 != null) {
|
||||
if (NetworkManager.getInstance().getLocalPlayerId() == null) {
|
||||
String playerId = UUID.randomUUID().toString();
|
||||
NetworkManager.getInstance().setLocalPlayerId(playerId);
|
||||
Gdx.app.log("Network", "设置玩家ID: " + playerId);
|
||||
}
|
||||
NetworkManager.getInstance().sendCharacterSelection(selectedFighter1.getName());
|
||||
game.setScreen(new GameScreen(game, selectedFighter1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
@@ -168,7 +171,7 @@ public class CharacterSelectScreen extends ScreenAdapter implements InputProcess
|
||||
}
|
||||
value = true;
|
||||
} else {
|
||||
//占坑说是
|
||||
// 占坑说是
|
||||
}
|
||||
if (i == Input.Keys.Z) {
|
||||
if (is1P) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package uno.mloluyu.desktop;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
|
||||
// 日志功能已移除
|
||||
|
||||
/**
|
||||
* Desktop 平台启动器
|
||||
*/
|
||||
@@ -10,6 +12,8 @@ public class DesktopLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 若需要异常抓取,可在此处添加简单的 Thread.setDefaultUncaughtExceptionHandler
|
||||
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
|
||||
float scale = 1.0F;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import uno.mloluyu.characters.Action;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
@@ -18,6 +19,9 @@ import uno.mloluyu.characters.SimpleFighter;
|
||||
import uno.mloluyu.characters.AdvancedFighter;
|
||||
import uno.mloluyu.network.NetworkManager;
|
||||
import uno.mloluyu.util.ClearScreen;
|
||||
import uno.mloluyu.perf.PerfMetrics;
|
||||
import uno.mloluyu.util.TimeStepLimiter;
|
||||
import uno.mloluyu.util.GameConstants;
|
||||
import uno.mloluyu.versatile.FighterController;
|
||||
|
||||
public class GameScreen extends ScreenAdapter {
|
||||
@@ -30,6 +34,37 @@ public class GameScreen extends ScreenAdapter {
|
||||
private SpriteBatch batch;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
private OrthographicCamera camera;
|
||||
private Texture background;
|
||||
// 世界尺寸(基于背景原始尺寸 * 缩放)。如果需要可读取 Texture 宽高后动态设。
|
||||
private float worldWidth;
|
||||
private float worldHeight;
|
||||
// 背景整体缩放倍数:>1 表示背景比屏幕大,可只显示局部
|
||||
private static final float BACKGROUND_SCALE = 1.5f;
|
||||
// 摄像机竖直偏移(正值=镜头上移,让玩家更靠下;这里改为较小的正值防止看不到下方)
|
||||
private static final float CAMERA_Y_OFFSET = 60f;
|
||||
// 允许相机向下多看到的底部扩展(不被 clamp 过早挡住),解决放大 hitbox 下半部分出框
|
||||
private static final float CAMERA_BOTTOM_MARGIN = -30f;
|
||||
// 平滑跟随的垂直插值系数(独立控制 y,防止瞬间跳)
|
||||
private static final float CAMERA_LERP_ALPHA = 0.12f;
|
||||
// ========== 摄像机缩放配置 ==========
|
||||
// 是否使用动态缩放(多人时根据距离自动拉远 / 靠近)
|
||||
private static final boolean CAMERA_DYNAMIC_ZOOM = true;
|
||||
// (保留)固定缩放模式开关 & 数值;若需要强制固定视角可把 CAMERA_DYNAMIC_ZOOM 设 false
|
||||
private static final boolean CAMERA_USE_FIXED_ZOOM = false;
|
||||
private static final float CAMERA_FIXED_ZOOM = 0.80f;
|
||||
// 动态缩放参数:最小与最大(OrthographicCamera: <1 视角更近,>1 更远)
|
||||
private static final float CAMERA_MIN_ZOOM = 0.55f; // 角色很近时
|
||||
private static final float CAMERA_MAX_ZOOM = 1.25f; // 距离很远时
|
||||
// 达到最大缩放所对应的“玩家距离”基准(屏幕世界单位,按你的角色移动范围调)
|
||||
private static final float CAMERA_MAX_DISTANCE = 1800f;
|
||||
// 缩放插值速度(越大越快贴近目标)
|
||||
private static final float CAMERA_ZOOM_LERP = 0.10f;
|
||||
// 显示地面参考线
|
||||
private static final boolean SHOW_GROUND_LINE = true;
|
||||
// 半透明地面条带显示
|
||||
private static final boolean SHOW_GROUND_STRIP = true;
|
||||
private static final float GROUND_STRIP_HEIGHT = 14f; // 条带厚度
|
||||
private static final float GROUND_STRIP_ALPHA = 0.20f; // 透明度 (0~1)
|
||||
|
||||
public GameScreen(MainGame game, SimpleFighter player) {
|
||||
this.player = player;
|
||||
@@ -38,18 +73,33 @@ public class GameScreen extends ScreenAdapter {
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
// 确保角色初始贴地(地面抬高后老存档/默认 0 需要调整)
|
||||
player.alignToGround();
|
||||
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
camera.position.set(player.getHitbox().x, player.getHitbox().y, 0); // 初始化摄像机位置
|
||||
// 初始化缩放:动态优先,否则固定
|
||||
if (CAMERA_DYNAMIC_ZOOM) {
|
||||
camera.zoom = CAMERA_MIN_ZOOM;
|
||||
} else if (CAMERA_USE_FIXED_ZOOM) {
|
||||
camera.zoom = CAMERA_FIXED_ZOOM;
|
||||
}
|
||||
camera.update();
|
||||
|
||||
batch = new SpriteBatch();
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
Gdx.input.setInputProcessor(controller);
|
||||
// 背景图(与主菜单共用 bg.png),可后续扩展成多关卡背景
|
||||
background = new Texture(Gdx.files.internal("innerbg.png"));
|
||||
worldWidth = background.getWidth() * BACKGROUND_SCALE;
|
||||
worldHeight = background.getHeight() * BACKGROUND_SCALE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
delta = TimeStepLimiter.clamp(delta);
|
||||
ClearScreen.clear();
|
||||
PerfMetrics.frame(delta);
|
||||
// (原先背景在摄像机更新前绘制,会出现一帧“滞后”错位;已移到摄像机更新后)
|
||||
|
||||
// 输入 / 逻辑
|
||||
player.update(delta);
|
||||
@@ -150,7 +200,7 @@ public class GameScreen extends ScreenAdapter {
|
||||
String pid = dt.getKey();
|
||||
// 重生位置简单放原点附近随机
|
||||
float rx = (float) (Math.random() * 200 - 100);
|
||||
float ry = 0;
|
||||
float ry = GameConstants.GROUND_Y;
|
||||
NetworkManager.getInstance().sendRespawn(pid, rx, ry);
|
||||
deathTimers.remove(pid);
|
||||
}
|
||||
@@ -197,6 +247,9 @@ public class GameScreen extends ScreenAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// 本地与远程玩家之间简单碰撞(防穿人)——放在摄像机更新前
|
||||
resolvePlayerCollisions();
|
||||
|
||||
// 摄像机跟随
|
||||
// 摄像头跟随:若有一个远程玩家,则居中于本地和远程玩家中点
|
||||
Vector3 targetPos;
|
||||
@@ -204,32 +257,87 @@ public class GameScreen extends ScreenAdapter {
|
||||
SimpleFighter remote = otherPlayers.values().iterator().next();
|
||||
float midX = (player.getHitbox().x + remote.getHitbox().x) * 0.5f;
|
||||
float midY = (player.getHitbox().y + remote.getHitbox().y) * 0.5f;
|
||||
targetPos = new Vector3(midX, midY, 0);
|
||||
targetPos = new Vector3(midX, midY + CAMERA_Y_OFFSET, 0);
|
||||
} else {
|
||||
// 默认为跟随本地玩家
|
||||
targetPos = new Vector3(player.getHitbox().x, player.getHitbox().y, 0);
|
||||
targetPos = new Vector3(player.getHitbox().x, player.getHitbox().y + CAMERA_Y_OFFSET, 0);
|
||||
}
|
||||
camera.position.lerp(targetPos, 0.1f);
|
||||
// 仅对 x,y 做分量插值,可单独调节速度
|
||||
camera.position.x += (targetPos.x - camera.position.x) * CAMERA_LERP_ALPHA;
|
||||
camera.position.y += (targetPos.y - camera.position.y) * (CAMERA_LERP_ALPHA * 1.1f);
|
||||
// 计算动态缩放目标
|
||||
if (CAMERA_DYNAMIC_ZOOM && otherPlayers.size() >= 1) {
|
||||
// 取所有玩家(本地 + 远程)x 坐标的最大跨度作为距离依据,可扩展为对角线距离
|
||||
float minX = player.getHitbox().x;
|
||||
float maxX = player.getHitbox().x;
|
||||
float minY = player.getHitbox().y;
|
||||
float maxY = player.getHitbox().y;
|
||||
for (SimpleFighter r : otherPlayers.values()) {
|
||||
Rectangle hb = r.getHitbox();
|
||||
if (hb.x < minX)
|
||||
minX = hb.x;
|
||||
if (hb.x > maxX)
|
||||
maxX = hb.x;
|
||||
if (hb.y < minY)
|
||||
minY = hb.y;
|
||||
if (hb.y > maxY)
|
||||
maxY = hb.y;
|
||||
}
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
// 这里主要横向对战,优先 dx;若想考虑纵向可用距离 = max(dx, dy*系数)
|
||||
float dist = Math.max(dx, dy * 0.6f);
|
||||
float t = Math.min(1f, dist / CAMERA_MAX_DISTANCE); // 0~1
|
||||
float targetZoom = CAMERA_MIN_ZOOM + (CAMERA_MAX_ZOOM - CAMERA_MIN_ZOOM) * t;
|
||||
camera.zoom += (targetZoom - camera.zoom) * CAMERA_ZOOM_LERP;
|
||||
} else if (CAMERA_USE_FIXED_ZOOM) {
|
||||
// 固定缩放平滑
|
||||
if (Math.abs(camera.zoom - CAMERA_FIXED_ZOOM) > 0.0001f) {
|
||||
camera.zoom += (CAMERA_FIXED_ZOOM - camera.zoom) * 0.25f;
|
||||
}
|
||||
}
|
||||
// 约束摄像机在世界边界内(视口以中心为基准)
|
||||
float halfW = (camera.viewportWidth * camera.zoom) / 2f;
|
||||
float halfH = (camera.viewportHeight * camera.zoom) / 2f;
|
||||
camera.position.x = Math.max(halfW, Math.min(worldWidth - halfW, camera.position.x));
|
||||
float minY = halfH - CAMERA_BOTTOM_MARGIN; // 允许比世界底部再低一些显示底部区域
|
||||
camera.position.y = Math.max(minY, Math.min(worldHeight - halfH, camera.position.y));
|
||||
camera.update();
|
||||
batch.setProjectionMatrix(camera.combined);
|
||||
shapeRenderer.setProjectionMatrix(camera.combined);
|
||||
|
||||
// -------- Background pass --------
|
||||
batch.begin();
|
||||
// 仅绘制背景的局部:通过在更大的缩放空间中直接拉伸整张图并限制摄像机
|
||||
// 若想真正裁剪一部分,可改用纹理区域;此处使用整图放大后让摄像机在其内游走
|
||||
batch.draw(background,
|
||||
0, 0,
|
||||
worldWidth, worldHeight);
|
||||
batch.end();
|
||||
|
||||
// 混合
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND);
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// -------- Filled pass --------
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
drawHitbox(player, Color.BLUE);
|
||||
// 半透明地面条带(先于 hitbox 绘制,这样人物在其上方)
|
||||
if (SHOW_GROUND_STRIP) {
|
||||
float y = GameConstants.GROUND_Y - GROUND_STRIP_HEIGHT * 0.5f; // 居中穿过地面线
|
||||
shapeRenderer.setColor(0.9f, 0.9f, 0.9f, GROUND_STRIP_ALPHA); // 近白轻薄层
|
||||
shapeRenderer.rect(0f, y, worldWidth, GROUND_STRIP_HEIGHT);
|
||||
}
|
||||
drawHitbox(player, player.getDebugColor());
|
||||
boolean showPlayerAttack = player.isAttacking()
|
||||
|| (player.getCurrentAction() == Action.ATTACK && player.getAttackTimer() > 0);
|
||||
if (showPlayerAttack)
|
||||
drawAttackBox(player, 1f, 0f, 0f, 0.35f);
|
||||
// 攻击框颜色改为蓝色(原红色)
|
||||
drawAttackBox(player, 0.0f, 0.45f, 1f, 0.35f);
|
||||
|
||||
for (SimpleFighter remote : otherPlayers.values()) {
|
||||
drawHitbox(remote, Color.GREEN);
|
||||
drawHitbox(remote, remote.getDebugColor());
|
||||
if (remote.isAttacking())
|
||||
drawAttackBox(remote, 1f, 0f, 0f, 0.25f);
|
||||
drawAttackBox(remote, 0.0f, 0.45f, 1f, 0.25f);
|
||||
}
|
||||
shapeRenderer.end();
|
||||
|
||||
@@ -240,11 +348,13 @@ public class GameScreen extends ScreenAdapter {
|
||||
|
||||
// -------- Debug line pass --------
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
|
||||
if (SHOW_GROUND_LINE) {
|
||||
shapeRenderer.setColor(0.65f, 0.65f, 0.65f, 1f); // 浅灰色参考线
|
||||
shapeRenderer.line(0f, GameConstants.GROUND_Y, worldWidth, GameConstants.GROUND_Y);
|
||||
}
|
||||
player.renderDebug(shapeRenderer);
|
||||
for (SimpleFighter remote : otherPlayers.values())
|
||||
remote.renderDebug(shapeRenderer);
|
||||
shapeRenderer.setColor(Color.WHITE);
|
||||
shapeRenderer.rect(0, 0, 1000, 1000);
|
||||
shapeRenderer.end();
|
||||
// -------- UI health bar pass --------
|
||||
// 使用屏幕坐标绘制血条
|
||||
@@ -274,18 +384,45 @@ public class GameScreen extends ScreenAdapter {
|
||||
idx++;
|
||||
}
|
||||
shapeRenderer.end();
|
||||
// 原定期性能日志已移除(日志系统删除)
|
||||
}
|
||||
|
||||
private void drawHitbox(SimpleFighter fighter, Color color) {
|
||||
shapeRenderer.setColor(color);
|
||||
Rectangle r = fighter.getHitbox();
|
||||
shapeRenderer.rect(r.x, r.y, r.width, r.height);
|
||||
float scale = GameConstants.DEBUG_BOX_SCALE;
|
||||
if (scale <= 1.0001f && scale >= 0.9999f) { // 视为 1
|
||||
shapeRenderer.rect(r.x, r.y, r.width, r.height);
|
||||
return;
|
||||
}
|
||||
float cx = r.x + r.width / 2f;
|
||||
float cy = r.y + r.height / 2f;
|
||||
float w = r.width * scale;
|
||||
float h = r.height * scale;
|
||||
if (GameConstants.DEBUG_SCALE_FROM_CENTER) {
|
||||
shapeRenderer.rect(cx - w / 2f, cy - h / 2f, w, h);
|
||||
} else {
|
||||
shapeRenderer.rect(r.x, r.y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAttackBox(SimpleFighter fighter, float r, float g, float b, float a) {
|
||||
shapeRenderer.setColor(r, g, b, a);
|
||||
Rectangle box = fighter.getAttackbox();
|
||||
shapeRenderer.rect(box.x, box.y, box.width, box.height);
|
||||
float scale = GameConstants.DEBUG_BOX_SCALE;
|
||||
if (scale <= 1.0001f && scale >= 0.9999f) {
|
||||
shapeRenderer.rect(box.x, box.y, box.width, box.height);
|
||||
return;
|
||||
}
|
||||
float cx = box.x + box.width / 2f;
|
||||
float cy = box.y + box.height / 2f;
|
||||
float w = box.width * scale;
|
||||
float h = box.height * scale;
|
||||
if (GameConstants.DEBUG_SCALE_FROM_CENTER) {
|
||||
shapeRenderer.rect(cx - w / 2f, cy - h / 2f, w, h);
|
||||
} else {
|
||||
shapeRenderer.rect(box.x, box.y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
// private void checkPlayerAttacks() {
|
||||
@@ -303,6 +440,41 @@ public class GameScreen extends ScreenAdapter {
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
shapeRenderer.dispose();
|
||||
if (background != null)
|
||||
background.dispose();
|
||||
NetworkManager.getInstance().disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解决本地玩家与每个远程玩家的水平重叠,避免“穿人”视觉效果。
|
||||
* 当前策略:仅移动本地玩家(不修改远程玩家坐标,避免产生需要网络回传的状态)。
|
||||
* 若需要更严格的对等碰撞,可在主机端双向分离并广播,但此处先满足基本需求。
|
||||
*/
|
||||
private void resolvePlayerCollisions() {
|
||||
if (otherPlayers.isEmpty())
|
||||
return;
|
||||
Rectangle a = player.getHitbox();
|
||||
for (SimpleFighter remote : otherPlayers.values()) {
|
||||
Rectangle b = remote.getHitbox();
|
||||
if (!a.overlaps(b))
|
||||
continue;
|
||||
// 仅考虑水平最小位移分离(2D 侧向格斗常见做法)
|
||||
float axCenter = a.x + a.width / 2f;
|
||||
float bxCenter = b.x + b.width / 2f;
|
||||
float dx = axCenter - bxCenter; // 正值:本地在右侧
|
||||
float overlapX = (a.width + b.width) / 2f - Math.abs(dx);
|
||||
if (overlapX > 0) {
|
||||
if (dx >= 0) {
|
||||
a.x += overlapX; // 本地向右推
|
||||
} else {
|
||||
a.x -= overlapX; // 本地向左推
|
||||
}
|
||||
// 世界边界限制(假设世界从 0 开始到 worldWidth)
|
||||
if (a.x < 0)
|
||||
a.x = 0;
|
||||
if (a.x + a.width > worldWidth)
|
||||
a.x = worldWidth - a.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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;
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
import uno.mloluyu.versatile.FighterController;
|
||||
|
||||
import static uno.mloluyu.util.Font.loadChineseFont;
|
||||
|
||||
public class MainMenuScreen extends ScreenAdapter {
|
||||
@@ -32,7 +30,7 @@ public class MainMenuScreen extends ScreenAdapter {
|
||||
|
||||
public MainMenuScreen(MainGame game) {
|
||||
this.game = game;
|
||||
texture = new Texture(Gdx.files.internal("src\\main\\resources\\bg.png"));
|
||||
texture = new Texture(Gdx.files.internal("bg.png"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +44,7 @@ public class MainMenuScreen extends ScreenAdapter {
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
Gdx.gl.glClearColor(0,0,0,0);
|
||||
Gdx.gl.glClearColor(0, 0, 0, 0);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
@@ -76,6 +74,7 @@ public class MainMenuScreen extends ScreenAdapter {
|
||||
game.setScreen(new CharacterSelectScreen(game));
|
||||
} else if (isTouched(mouseX, mouseY, buttonX, settingsY)) {
|
||||
Gdx.app.log("Button", "设置按钮被点击!");
|
||||
game.setScreen(new SettingsScreen(game));
|
||||
} else if (isTouched(mouseX, mouseY, buttonX, networkY)) {
|
||||
Gdx.app.log("Button", "联网设置按钮被点击!");
|
||||
game.setScreen(new NetworkSettingsScreen(game));
|
||||
@@ -92,8 +91,7 @@ public class MainMenuScreen extends ScreenAdapter {
|
||||
}
|
||||
|
||||
private void drawButtonText(int y, String text) {
|
||||
float textWidth = font.getRegion().getRegionWidth(); // 粗略估算
|
||||
float textX = buttonX + buttonWidth / 2f - text.length() * 20; // 居中估算
|
||||
float textX = buttonX + buttonWidth / 2f - text.length() * 20; // 简单估算居中
|
||||
float textY = y + buttonHeight / 2f + 20;
|
||||
font.draw(batch, text, textX, textY);
|
||||
}
|
||||
@@ -104,8 +102,11 @@ public class MainMenuScreen extends ScreenAdapter {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (batch != null) batch.dispose();
|
||||
if (font != null) font.dispose();
|
||||
if (shapeRenderer != null) shapeRenderer.dispose();
|
||||
if (batch != null)
|
||||
batch.dispose();
|
||||
if (font != null)
|
||||
font.dispose();
|
||||
if (shapeRenderer != null)
|
||||
shapeRenderer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
@@ -40,7 +39,8 @@ public class NetworkSettingsScreen extends ScreenAdapter {
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
new ClearScreen();
|
||||
// 使用静态工具方法清屏
|
||||
ClearScreen.clear();
|
||||
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
@@ -64,6 +64,12 @@ public class NetworkSettingsScreen extends ScreenAdapter {
|
||||
drawButtonText(CREATE_ROOM_Y, "创建房间");
|
||||
drawButtonText(JOIN_ROOM_Y, "加入房间");
|
||||
drawButtonText(EXIT_Y, "返回");
|
||||
// 状态信息
|
||||
NetworkManager nm = NetworkManager.getInstance();
|
||||
String id = nm.getLocalPlayerId();
|
||||
font.draw(batch, "本机ID: " + (id == null ? "(未分配)" : id.substring(0, Math.min(8, id.length()))), 50, 200);
|
||||
font.draw(batch, nm.isHost() ? "当前: 房主" : (nm.isConnected() ? "当前: 已连接客户端" : "当前: 未连接"), 50, 160);
|
||||
font.draw(batch, "在线玩家: " + (nm.getPlayerPositions() == null ? 0 : nm.getPlayerPositions().size()), 50, 120);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
|
||||
25
src/main/java/uno/mloluyu/desktop/ScreenManager.java
Normal file
25
src/main/java/uno/mloluyu/desktop/ScreenManager.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package uno.mloluyu.desktop;
|
||||
|
||||
/** 简易屏幕管理器,负责切换与异常保护。 */
|
||||
public class ScreenManager {
|
||||
private final MainGame game;
|
||||
private BaseScreen current;
|
||||
|
||||
public ScreenManager(MainGame game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public void set(BaseScreen next) {
|
||||
try {
|
||||
if (current != null) {
|
||||
current.hide();
|
||||
current.dispose();
|
||||
}
|
||||
current = next;
|
||||
game.setScreen(next);
|
||||
} catch (Throwable t) {
|
||||
// 忽略或可加简单 System.err
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/main/java/uno/mloluyu/desktop/SettingsScreen.java
Normal file
75
src/main/java/uno/mloluyu/desktop/SettingsScreen.java
Normal file
@@ -0,0 +1,75 @@
|
||||
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.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import uno.mloluyu.util.ClearScreen;
|
||||
import static uno.mloluyu.util.Font.loadChineseFont;
|
||||
|
||||
/**
|
||||
* 简单设置界面占位:未来可扩展(音量/按键设置)。
|
||||
*/
|
||||
public class SettingsScreen extends ScreenAdapter {
|
||||
private final MainGame game;
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
private ShapeRenderer shapeRenderer;
|
||||
|
||||
private static final int BACK_X = 100; // 返回按钮区域
|
||||
private static final int BACK_Y = 100;
|
||||
private static final int BACK_W = 220;
|
||||
private static final int BACK_H = 70;
|
||||
|
||||
public SettingsScreen(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) {
|
||||
ClearScreen.clear();
|
||||
int mouseX = Gdx.input.getX();
|
||||
int mouseY = Gdx.graphics.getHeight() - Gdx.input.getY();
|
||||
|
||||
// 绘制背景与返回按钮
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
shapeRenderer.setColor(Color.DARK_GRAY);
|
||||
shapeRenderer.rect(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
boolean backHover = isHovered(mouseX, mouseY, BACK_X, BACK_Y, BACK_W, BACK_H);
|
||||
shapeRenderer.setColor(backHover ? Color.LIGHT_GRAY : Color.GRAY);
|
||||
shapeRenderer.rect(BACK_X, BACK_Y, BACK_W, BACK_H);
|
||||
shapeRenderer.end();
|
||||
|
||||
batch.begin();
|
||||
font.draw(batch, "设置 (占位界面)", 100, Gdx.graphics.getHeight() - 120);
|
||||
font.draw(batch, "此处可添加: 音量 / 按键 / 分辨率 / 语言 等", 100, Gdx.graphics.getHeight() - 180);
|
||||
font.draw(batch, "返回", BACK_X + 50, BACK_Y + 45);
|
||||
batch.end();
|
||||
|
||||
if (Gdx.input.justTouched() && backHover) {
|
||||
game.setScreen(new MainMenuScreen(game));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
font.dispose();
|
||||
shapeRenderer.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
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;
|
||||
import uno.mloluyu.util.ResourcePaths;
|
||||
|
||||
/**
|
||||
* 启动屏幕类
|
||||
* 显示游戏Logo并在3秒后切换到主菜单界面
|
||||
*/
|
||||
public class StartScreen implements Screen {
|
||||
public class StartScreen extends BaseScreen {
|
||||
|
||||
private MainGame mainGame;
|
||||
private MainGame mainGame; // TODO: 后续可直接用BaseScreen.game
|
||||
|
||||
private Texture logoTexture;
|
||||
private com.badlogic.gdx.graphics.g2d.SpriteBatch batch;
|
||||
@@ -19,8 +19,9 @@ public class StartScreen implements Screen {
|
||||
private float deltaSum;
|
||||
|
||||
public StartScreen(MainGame mainGame) {
|
||||
super(mainGame);
|
||||
this.mainGame = mainGame;
|
||||
logoTexture = new Texture(Gdx.files.internal("logo.png"));
|
||||
logoTexture = new Texture(Gdx.files.internal(ResourcePaths.LOGO));
|
||||
batch = new com.badlogic.gdx.graphics.g2d.SpriteBatch();
|
||||
}
|
||||
|
||||
@@ -36,7 +37,6 @@ public class StartScreen implements Screen {
|
||||
if (deltaSum >= .01F) {
|
||||
if (mainGame != null) {
|
||||
mainGame.showGameScreen();
|
||||
System.out.println("已经切换到主菜单");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,14 @@ public class NetworkManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地玩家所选角色(仅本地缓存,远程映射存于 playerCharacters)。
|
||||
* 供界面或后续同步逻辑查询。
|
||||
*/
|
||||
public String getLocalCharacter() {
|
||||
return localCharacter;
|
||||
}
|
||||
|
||||
public void receiveMessage(String message) {// 解析消息
|
||||
|
||||
if (message.startsWith("POS:")) {
|
||||
|
||||
20
src/main/java/uno/mloluyu/perf/PerfMetrics.java
Normal file
20
src/main/java/uno/mloluyu/perf/PerfMetrics.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package uno.mloluyu.perf;
|
||||
|
||||
/** 简单性能指标收集(首版)。 */
|
||||
public final class PerfMetrics {
|
||||
private static long frameCount;
|
||||
private static double accumTime;
|
||||
private static double maxFrame = 0;
|
||||
|
||||
public static void frame(double delta) {
|
||||
frameCount++;
|
||||
accumTime += delta;
|
||||
if (delta > maxFrame)
|
||||
maxFrame = delta;
|
||||
}
|
||||
|
||||
public static String summary() {
|
||||
double avg = frameCount == 0 ? 0 : accumTime / frameCount;
|
||||
return String.format("frames=%d avg=%.4f max=%.4f", frameCount, avg, maxFrame);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ package uno.mloluyu.util;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class ClearScreen {
|
||||
public ClearScreen() {
|
||||
/** 清屏工具:改为静态方法避免每帧 new 对象。 */
|
||||
public final class ClearScreen {
|
||||
private ClearScreen() {
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
Gdx.gl.glClearColor(0.3F, 0.3F, 0.5F, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
src/main/java/uno/mloluyu/util/GameConstants.java
Normal file
32
src/main/java/uno/mloluyu/util/GameConstants.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package uno.mloluyu.util;
|
||||
|
||||
/**
|
||||
* 全局游戏常量集中放置。
|
||||
*/
|
||||
public final class GameConstants {
|
||||
/**
|
||||
* 地面(空气墙)Y 坐标。抬高后便于更好构图。
|
||||
*/
|
||||
public static final float GROUND_Y = 180f; // 可按需要再调
|
||||
|
||||
// 角色移动/物理参数(集中配置便于统一手感调节)
|
||||
// 用户要求:速度 *2,跳跃 *3(基于当前 520 / 1250)
|
||||
public static final float MOVE_SPEED = 1040f; // 520 *2 (最初 ~300)
|
||||
// 调低跳跃:更低高度 + 更短滞空:v0 ↓,同时重力 ↑
|
||||
// 说明:上升时间 t_up = v0 / g,本次取 v0=1500, g=3200 =>
|
||||
// t_up≈0.47s,总滞空≈0.94s,高度≈(v0^2)/(2g)≈351
|
||||
// 若想再更低:JUMP_SPEED 1400 + GRAVITY 3400;再更高一点:JUMP_SPEED 1600 + GRAVITY 3000。
|
||||
public static final float JUMP_SPEED = 1500f;
|
||||
public static final float GRAVITY = 3200f; // 加大重力让落地更快
|
||||
|
||||
// 调试命中盒渲染缩放(=1 表示真实大小;之前放大 3.6 现在回归可控)
|
||||
// 调试盒缩放:1 = 实际大小;若想放大显示结构,可调大。
|
||||
public static final float DEBUG_BOX_SCALE = 1.0f;
|
||||
// 是否按中心放大(true 则保持角色中心位置,不会视觉漂移)
|
||||
public static final boolean DEBUG_SCALE_FROM_CENTER = true;
|
||||
|
||||
// (可选)相机或后续平衡参数也可集中放这里
|
||||
|
||||
private GameConstants() {
|
||||
}
|
||||
}
|
||||
12
src/main/java/uno/mloluyu/util/ResourcePaths.java
Normal file
12
src/main/java/uno/mloluyu/util/ResourcePaths.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package uno.mloluyu.util;
|
||||
|
||||
/** 统一资源常量,避免魔法字符串散落。 */
|
||||
public final class ResourcePaths {
|
||||
private ResourcePaths() {
|
||||
}
|
||||
|
||||
public static final String LOGO = "logo.png";
|
||||
public static final String FONT_MAIN = "FLyouzichati-Regular-2.ttf";
|
||||
public static final String CHARACTER_ROOT = "character/";
|
||||
public static final String UI_SKIN_JSON = "ui/uiskin.json";
|
||||
}
|
||||
13
src/main/java/uno/mloluyu/util/TimeStepLimiter.java
Normal file
13
src/main/java/uno/mloluyu/util/TimeStepLimiter.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package uno.mloluyu.util;
|
||||
|
||||
/** 限制delta时间,防止窗口拖拽/卡顿后出现物理跳跃。 */
|
||||
public final class TimeStepLimiter {
|
||||
private TimeStepLimiter() {
|
||||
}
|
||||
|
||||
private static final float MAX_DELTA = 1f / 30f; // 上限: 相当于最低30FPS
|
||||
|
||||
public static float clamp(float delta) {
|
||||
return delta > MAX_DELTA ? MAX_DELTA : Math.max(delta, 0f);
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/innerbg.png
Normal file
BIN
src/main/resources/innerbg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
22
src/test/java/uno/mloluyu/assets/AssetsExistenceTest.java
Normal file
22
src/test/java/uno/mloluyu/assets/AssetsExistenceTest.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package uno.mloluyu.assets;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class AssetsExistenceTest {
|
||||
private static final Path RES = Path.of("src", "main", "resources");
|
||||
|
||||
@Test
|
||||
void testCoreAssetsPresent() {
|
||||
List<String> required = List.of("logo.png", "character/alice/alice.png", "character/reimu/reimu-0.png",
|
||||
"ui/uiskin.json");
|
||||
for (String r : required) {
|
||||
Path p = RES.resolve(r);
|
||||
Assertions.assertTrue(Files.exists(p), "缺失资源: " + r);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/test/java/uno/mloluyu/characters/ActionStateTest.java
Normal file
21
src/test/java/uno/mloluyu/characters/ActionStateTest.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package uno.mloluyu.characters;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ActionStateTest {
|
||||
@Test
|
||||
void testLegalTransitionIdleToMove() {
|
||||
SimpleFighter f = new SimpleFighter("Test");
|
||||
f.changeAction(Action.MOVE);
|
||||
Assertions.assertEquals(Action.MOVE, f.getCurrentAction());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegalTransitionDeadToMove() {
|
||||
SimpleFighter f = new SimpleFighter("Test");
|
||||
f.changeAction(Action.DEAD);
|
||||
f.changeAction(Action.MOVE); // 应被拒绝
|
||||
Assertions.assertEquals(Action.DEAD, f.getCurrentAction());
|
||||
}
|
||||
}
|
||||
BIN
target/classes/innerbg.png
Normal file
BIN
target/classes/innerbg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
Binary file not shown.
BIN
target/classes/uno/mloluyu/characters/ActionStateGuard.class
Normal file
BIN
target/classes/uno/mloluyu/characters/ActionStateGuard.class
Normal file
Binary file not shown.
BIN
target/classes/uno/mloluyu/characters/ActionTransitionMap.class
Normal file
BIN
target/classes/uno/mloluyu/characters/ActionTransitionMap.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/characters/FighterBase.class
Normal file
BIN
target/classes/uno/mloluyu/characters/FighterBase.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/desktop/BaseScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/BaseScreen.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/desktop/ScreenManager.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/ScreenManager.class
Normal file
Binary file not shown.
BIN
target/classes/uno/mloluyu/desktop/SettingsScreen.class
Normal file
BIN
target/classes/uno/mloluyu/desktop/SettingsScreen.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/perf/PerfMetrics.class
Normal file
BIN
target/classes/uno/mloluyu/perf/PerfMetrics.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/util/GameConstants.class
Normal file
BIN
target/classes/uno/mloluyu/util/GameConstants.class
Normal file
Binary file not shown.
BIN
target/classes/uno/mloluyu/util/ResourcePaths.class
Normal file
BIN
target/classes/uno/mloluyu/util/ResourcePaths.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/uno/mloluyu/util/TimeStepLimiter.class
Normal file
BIN
target/classes/uno/mloluyu/util/TimeStepLimiter.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/game-1.0-SNAPSHOT.jar
Normal file
BIN
target/game-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
3
target/maven-archiver/pom.properties
Normal file
3
target/maven-archiver/pom.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
artifactId=game
|
||||
groupId=uno.mloluyu
|
||||
version=1.0-SNAPSHOT
|
||||
@@ -1,6 +1,7 @@
|
||||
uno\mloluyu\desktop\StartScreen.class
|
||||
uno\mloluyu\desktop\NetworkSettingsScreen$1.class
|
||||
uno\mloluyu\network\NetworkManager.class
|
||||
uno\mloluyu\characters\FighterAnimationManager.class
|
||||
uno\mloluyu\desktop\CharacterSelectScreen.class
|
||||
uno\mloluyu\versatile\FighterController.class
|
||||
uno\mloluyu\desktop\MainGame.class
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\Action.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\ActionStateGuard.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\ActionTransitionMap.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\AdvancedFighter.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\FighterAnimationManager.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\FighterBase.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\characters\SimpleFighter.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\BaseScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\CharacterSelectScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\DesktopLauncher.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\GameScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\MainGame.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\desktop\NetworkSettingsScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\ScreenManager.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\SettingsScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\desktop\StartScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\network\ConnectClient.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\NetworkManager.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\perf\PerfMetrics.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\ClearScreen.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\Font.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\GameConstants.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\ResourcePaths.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\SimpleFormatter.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\util\TimeStepLimiter.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\main\java\uno\mloluyu\versatile\FighterController.java
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\test\java\uno\mloluyu\assets\AssetsExistenceTest.java
|
||||
C:\Users\www\Documents\Game\格斗游戏\Game\src\test\java\uno\mloluyu\characters\ActionStateTest.java
|
||||
BIN
target/test-classes/uno/mloluyu/assets/AssetsExistenceTest.class
Normal file
BIN
target/test-classes/uno/mloluyu/assets/AssetsExistenceTest.class
Normal file
Binary file not shown.
BIN
target/test-classes/uno/mloluyu/characters/ActionStateTest.class
Normal file
BIN
target/test-classes/uno/mloluyu/characters/ActionStateTest.class
Normal file
Binary file not shown.
Reference in New Issue
Block a user