init
This commit is contained in:
14
TODO
Normal file
14
TODO
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
タイルの当たり判定の実装 - DONE
|
||||||
|
└バグ修正 - DONE
|
||||||
|
敵の生成 - WIP
|
||||||
|
└複数をランダムの位置に生成 - DONE
|
||||||
|
└キャラクタ同士の当たり判定 - WIP
|
||||||
|
└敵の行動AI - WIP
|
||||||
|
UIの実装
|
||||||
|
┗ステータスの表示 - DONE
|
||||||
|
┗ボタンの実装 - WIP
|
||||||
|
┗セリフの表示 - NONE
|
||||||
|
|
||||||
|
使用素材
|
||||||
|
https://absuredmrsteel.itch.io/16x16-home-interior-pixel-art
|
||||||
|
https://opengameart.org/content/rpg-asset-character-prince-nes
|
||||||
1
build.bat
Normal file
1
build.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
javac -Xlint:deprecation -Xdiags:verbose -Xlint:unchecked -deprecation -d bin -encoding utf8 -classpath res -sourcepath src src/Launcher.java
|
||||||
BIN
res/character/Full_SpriteSheet.png
Normal file
BIN
res/character/Full_SpriteSheet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
res/default.png
Normal file
BIN
res/default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 584 B |
BIN
res/tileset/House_Tileset.png
Normal file
BIN
res/tileset/House_Tileset.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
4
res/tileset/House_Tileset.tsx
Normal file
4
res/tileset/House_Tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.8" tiledversion="1.8.2" name="House_Tileset" tilewidth="16" tileheight="16" tilecount="54" columns="9">
|
||||||
|
<image source="House_Tileset.png" width="144" height="96"/>
|
||||||
|
</tileset>
|
||||||
100
res/tileset/map1.tmx
Normal file
100
res/tileset/map1.tmx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="20" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="5" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="House_Tileset.tsx"/>
|
||||||
|
<layer id="1" name="タイルレイヤー1" width="20" height="20" visible="0">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="2" name="タイルレイヤー2" width="20" height="20">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,20,0,0,0,27,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,37,38,39,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,46,47,48,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,28,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="3" name="タイルレイヤー3" width="20" height="20">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="4" name="タイルレイヤー4" width="20" height="20">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
12
src/Launcher.java
Normal file
12
src/Launcher.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import game.GameLoop;
|
||||||
|
|
||||||
|
public class Launcher {
|
||||||
|
private Launcher() {
|
||||||
|
Thread thread = new Thread(new GameLoop());
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Launcher();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/ai/AICondition.java
Normal file
8
src/ai/AICondition.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package ai;
|
||||||
|
|
||||||
|
import entity.Entity;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public interface AICondition {
|
||||||
|
boolean isMet(State state, Entity entity);
|
||||||
|
}
|
||||||
38
src/ai/AIManager.java
Normal file
38
src/ai/AIManager.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package ai;
|
||||||
|
|
||||||
|
import ai.state.AIState;
|
||||||
|
import ai.state.Stand;
|
||||||
|
import ai.state.Wander;
|
||||||
|
import entity.Entity;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class AIManager {
|
||||||
|
private AIState currentAIState;
|
||||||
|
|
||||||
|
public AIManager() {
|
||||||
|
transitionTo("stand");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AIState getCurrentAIState() {
|
||||||
|
return currentAIState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transitionTo(String nextState) {
|
||||||
|
switch (nextState) {
|
||||||
|
case "wander":
|
||||||
|
currentAIState = new Wander();
|
||||||
|
break;
|
||||||
|
case "stand":
|
||||||
|
currentAIState = new Stand();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(State state, Entity entity) {
|
||||||
|
currentAIState.update(state, entity);
|
||||||
|
if (currentAIState.shouldTransition(state, entity)) {
|
||||||
|
transitionTo(currentAIState.getNextState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/ai/AITransition.java
Normal file
22
src/ai/AITransition.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package ai;
|
||||||
|
|
||||||
|
import entity.Entity;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class AITransition {
|
||||||
|
private String nextState;
|
||||||
|
private AICondition condition;
|
||||||
|
|
||||||
|
public AITransition(String nextState, AICondition condition) {
|
||||||
|
this.nextState = nextState;
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldTransition(State state, Entity entity) {
|
||||||
|
return condition.isMet(state, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNextState() {
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/ai/state/AIState.java
Normal file
25
src/ai/state/AIState.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package ai.state;
|
||||||
|
|
||||||
|
import ai.AITransition;
|
||||||
|
import entity.Entity;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public abstract class AIState {
|
||||||
|
private AITransition transition;
|
||||||
|
|
||||||
|
public AIState() {
|
||||||
|
transition = initTransition();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AITransition initTransition();
|
||||||
|
|
||||||
|
public String getNextState() {
|
||||||
|
return transition.getNextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldTransition(State state, Entity entity) {
|
||||||
|
return transition.shouldTransition(state, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void update(State state, Entity entity);
|
||||||
|
}
|
||||||
24
src/ai/state/Stand.java
Normal file
24
src/ai/state/Stand.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package ai.state;
|
||||||
|
|
||||||
|
import ai.AITransition;
|
||||||
|
import entity.Entity;
|
||||||
|
import game.GameLoop;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Stand extends AIState {
|
||||||
|
private int alive;
|
||||||
|
|
||||||
|
public Stand() {
|
||||||
|
this.alive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITransition initTransition() {
|
||||||
|
return new AITransition("wander", ((state, eneity) -> alive >= GameLoop.ups * 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state, Entity entity) {
|
||||||
|
alive++;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/ai/state/Wander.java
Normal file
38
src/ai/state/Wander.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package ai.state;
|
||||||
|
|
||||||
|
import ai.AITransition;
|
||||||
|
import controller.EnemyController;
|
||||||
|
import core.Position;
|
||||||
|
import entity.Entity;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Wander extends AIState {
|
||||||
|
private Position target;
|
||||||
|
|
||||||
|
public Wander() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean arrived(Entity entity) {
|
||||||
|
return target != null && entity.getPosition().isInRangeOf(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AITransition initTransition() {
|
||||||
|
return new AITransition("stand", ((state, entity) -> arrived(entity)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state, Entity entity) {
|
||||||
|
EnemyController controller = (EnemyController)entity.getController();
|
||||||
|
if (target == null) {
|
||||||
|
target = state.getGameMap().findRandomPosition();
|
||||||
|
}
|
||||||
|
if (arrived(entity)) {
|
||||||
|
controller.stop();
|
||||||
|
}
|
||||||
|
if (target != null) {
|
||||||
|
controller.moveToTarget(target, entity.getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/controller/EnemyController.java
Normal file
43
src/controller/EnemyController.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package controller;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
|
||||||
|
public class EnemyController implements EntityController {
|
||||||
|
private boolean up;
|
||||||
|
private boolean down;
|
||||||
|
private boolean right;
|
||||||
|
private boolean left;
|
||||||
|
|
||||||
|
public boolean isRequestKeyAction() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestKeyDown() {
|
||||||
|
return down;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestKeyLeft() {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestKeyRight() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestKeyUp() {
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveToTarget(Position target, Position current) {
|
||||||
|
double deltaX = target.getX() - current.getX();
|
||||||
|
double deltaY = target.getY() - current.getY();
|
||||||
|
up = deltaY < 0 && Math.abs(deltaY) > Position.PROXIMITY_RANGE;
|
||||||
|
right = deltaX > 0 && Math.abs(deltaX) > Position.PROXIMITY_RANGE;
|
||||||
|
down = deltaY > 0 && Math.abs(deltaY) > Position.PROXIMITY_RANGE;
|
||||||
|
left = deltaX < 0 && Math.abs(deltaX) > Position.PROXIMITY_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
up = down = left = right = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/controller/EntityController.java
Normal file
9
src/controller/EntityController.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package controller;
|
||||||
|
|
||||||
|
public interface EntityController {
|
||||||
|
boolean isRequestKeyAction();
|
||||||
|
boolean isRequestKeyDown();
|
||||||
|
boolean isRequestKeyLeft();
|
||||||
|
boolean isRequestKeyRight();
|
||||||
|
boolean isRequestKeyUp();
|
||||||
|
}
|
||||||
58
src/core/Position.java
Normal file
58
src/core/Position.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package core;
|
||||||
|
|
||||||
|
public class Position {
|
||||||
|
public static final int PROXIMITY_RANGE = 3;
|
||||||
|
|
||||||
|
private double x;
|
||||||
|
private double y;
|
||||||
|
|
||||||
|
public Position() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(double x, double y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(Position position) {
|
||||||
|
x = position.getX();
|
||||||
|
y = position.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Vector2D vector) {
|
||||||
|
x += vector.getX();
|
||||||
|
y += vector.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInRangeOf(Position position) {
|
||||||
|
return Math.abs(x - position.getX()) < PROXIMITY_RANGE && Math.abs(y - position.getY()) < PROXIMITY_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(double x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(double y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "POSITION: (X: " + x + ", Y: " + y + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/core/Size.java
Normal file
40
src/core/Size.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package core;
|
||||||
|
|
||||||
|
public class Size {
|
||||||
|
private int height;
|
||||||
|
private int width;
|
||||||
|
|
||||||
|
public Size() {
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size(int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size(Size size) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int height) {
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int width) {
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SIZE: (W: " + width + ", H: " + height + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/core/Vector2D.java
Normal file
72
src/core/Vector2D.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package core;
|
||||||
|
|
||||||
|
public class Vector2D {
|
||||||
|
private double x;
|
||||||
|
private double y;
|
||||||
|
|
||||||
|
public Vector2D() {
|
||||||
|
this(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2D(double x, double y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2D copyOf(Vector2D vector) {
|
||||||
|
return new Vector2D(vector.getX(), vector.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2D directionBetweenPositions(Position start, Position end) {
|
||||||
|
Vector2D direction = new Vector2D(start.getX() - end.getX(), start.getY() - end.getY());
|
||||||
|
direction.normalize();
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double dotProduct(Vector2D v1, Vector2D v2) {
|
||||||
|
return v1.getX() * v2.getX() + v1.getY() * v2.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double length() {
|
||||||
|
return Math.sqrt(x * x + y * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multiply(double speed) {
|
||||||
|
x *= speed;
|
||||||
|
y *= speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void normalize() {
|
||||||
|
double length = length();
|
||||||
|
x = x == 0 ? 0 : x/length;
|
||||||
|
y = y == 0 ? 0 : y/length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotateByDegrees(int degrees) {
|
||||||
|
double radians = Math.toRadians(degrees);
|
||||||
|
double cosAngle = Math.cos(radians);
|
||||||
|
double sinAngle = Math.sin(radians);
|
||||||
|
double rotatedX = cosAngle * x - sinAngle * y;
|
||||||
|
double rotatedY = sinAngle * x + cosAngle * y;
|
||||||
|
x = rotatedX;
|
||||||
|
y = rotatedY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVector(double x, double y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "VECTOR: (X: " + x + ", Y: " + y + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/display/DebugRenderer.java
Normal file
74
src/display/DebugRenderer.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package display;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import entity.creature.Enemy;
|
||||||
|
import entity.creature.Player;
|
||||||
|
import game.GameLoop;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.PlayState;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class DebugRenderer {
|
||||||
|
public void renderDebug(Graphics2D g, State state) {
|
||||||
|
renderMapDataCollisionBox(g, state);
|
||||||
|
renderDebugCollisionBox(g, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderDebugCollisionBox(Graphics2D g, State state) {
|
||||||
|
g.setColor(Color.RED);
|
||||||
|
g.setStroke(new BasicStroke(2));
|
||||||
|
if (state instanceof PlayState playState) {
|
||||||
|
Position cp = state.getGameCamera().getPosition();
|
||||||
|
Player p = playState.getPlayer();
|
||||||
|
int scale = state.getGameMap().getScale();
|
||||||
|
playState.getEntities().stream().forEach(entity -> {
|
||||||
|
g.drawRect(
|
||||||
|
(int)(entity.getCollisionBox().getX() - cp.getX()), (int)(entity.getCollisionBox().getY() - cp.getY()),
|
||||||
|
(int)entity.getCollisionBox().getWidth(), (int)entity.getCollisionBox().getHeight());
|
||||||
|
if (entity instanceof Enemy enemy) {
|
||||||
|
g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 30));
|
||||||
|
g.setColor(Color.GRAY);
|
||||||
|
g.drawString(enemy.getCurrentState(), (int)(entity.getX() - cp.getX() + 2), (int)(entity.getY() - 8 - cp.getY()));
|
||||||
|
g.setColor(Color.WHITE);
|
||||||
|
g.drawString(enemy.getCurrentState(), (int)(entity.getX() - cp.getX()), (int)(entity.getY() - 10 - cp.getY()));
|
||||||
|
g.setColor(Color.RED);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
g.drawRect(
|
||||||
|
(int)(p.getCollisionBox().getX() - cp.getX()), (int)(p.getCollisionBox().getY() - cp.getY()),
|
||||||
|
(int)p.getCollisionBox().getWidth(), (int)p.getCollisionBox().getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderMapDataCollisionBox(Graphics2D g, State state) {
|
||||||
|
Color color = Color.RED;
|
||||||
|
GameMap gm = state.getGameMap();
|
||||||
|
Size tileSize = gm.getTileSize();
|
||||||
|
Position cp = state.getGameCamera().getPosition();
|
||||||
|
int scale = gm.getScale();
|
||||||
|
int[][] mapData = gm.getMapData().get(GameMap.MAP_LAYER_4);
|
||||||
|
for (int y = 0; y < mapData.length; y++) {
|
||||||
|
for (int x = 0; x < mapData[y].length; x++) {
|
||||||
|
color = mapData[y][x] == 0 ? Color.YELLOW : Color.GREEN;
|
||||||
|
g.setColor(color);
|
||||||
|
g.drawRect(x * tileSize.getWidth() * scale - (int) cp.getX(), y * tileSize.getHeight() * scale - (int) cp.getY(),
|
||||||
|
tileSize.getWidth() * scale, tileSize.getHeight() * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state instanceof PlayState playState) {
|
||||||
|
if (gm.getTileRect().x != -1) {
|
||||||
|
Player player = playState.getPlayer();
|
||||||
|
g.setColor(player.isWalkable() ? Color.BLUE : Color.RED);
|
||||||
|
g.setStroke(new BasicStroke(3));
|
||||||
|
g.drawRect(gm.getTileRect().x - (int) cp.getX(), gm.getTileRect().y - (int) cp.getY(), tileSize.getWidth() * scale, tileSize.getHeight() * scale);
|
||||||
|
g.drawRect(gm.getTileRect2().x - (int) cp.getX(), gm.getTileRect2().y - (int) cp.getY(), tileSize.getWidth() * scale, tileSize.getHeight() * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/display/Display.java
Normal file
82
src/display/Display.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package display;
|
||||||
|
|
||||||
|
import java.awt.Canvas;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.image.BufferStrategy;
|
||||||
|
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import input.Keyboard;
|
||||||
|
import input.Mouse;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Display extends JFrame {
|
||||||
|
public static boolean debug;
|
||||||
|
private Canvas canvas;
|
||||||
|
private Keyboard keyboard;
|
||||||
|
private Mouse mouse;
|
||||||
|
private Renderer renderer;
|
||||||
|
private DebugRenderer debugRenderer;
|
||||||
|
|
||||||
|
public Display(Keyboard keyboard, Mouse mouse, String title, Size windowSize) {
|
||||||
|
super(title);
|
||||||
|
this.keyboard = keyboard;
|
||||||
|
this.mouse = mouse;
|
||||||
|
renderer = new Renderer();
|
||||||
|
debugRenderer = new DebugRenderer();
|
||||||
|
createFrame(windowSize);
|
||||||
|
createCanvas(windowSize);
|
||||||
|
add(canvas);
|
||||||
|
pack();
|
||||||
|
setVisible(true);
|
||||||
|
canvas.createBufferStrategy(2);
|
||||||
|
addKeyListener(this.keyboard);
|
||||||
|
canvas.addKeyListener(this.keyboard);
|
||||||
|
canvas.addMouseListener(this.mouse);
|
||||||
|
canvas.addMouseMotionListener(this.mouse);
|
||||||
|
canvas.setFocusable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCanvas(Size windowSize) {
|
||||||
|
GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
|
canvas = new Canvas(config);
|
||||||
|
Dimension dimension = new Dimension(windowSize.getWidth(), windowSize.getHeight());
|
||||||
|
canvas.setPreferredSize(dimension);
|
||||||
|
canvas.setMaximumSize(dimension);
|
||||||
|
canvas.setMinimumSize(dimension);
|
||||||
|
canvas.setBackground(Color.BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFrame(Size windowSize) {
|
||||||
|
setSize(windowSize.getWidth(), windowSize.getHeight());
|
||||||
|
setUndecorated(true);
|
||||||
|
setResizable(false);
|
||||||
|
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getCanvasSize() {
|
||||||
|
return new Size(canvas.getWidth(), canvas.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(State state) {
|
||||||
|
BufferStrategy bs = canvas.getBufferStrategy();
|
||||||
|
if (bs == null) {
|
||||||
|
canvas.createBufferStrategy(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
|
||||||
|
g.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
renderer.render(g, state);
|
||||||
|
if (debug) {
|
||||||
|
debugRenderer.renderDebug(g, state);
|
||||||
|
}
|
||||||
|
g.dispose();
|
||||||
|
bs.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/display/GameCamera.java
Normal file
55
src/display/GameCamera.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package display;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import entity.creature.Player;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class GameCamera {
|
||||||
|
private Optional<Player> focusObject;
|
||||||
|
private Position position;
|
||||||
|
private Size windowSize;
|
||||||
|
private Rectangle viewBounds;
|
||||||
|
|
||||||
|
public GameCamera(Size windowSize) {
|
||||||
|
this.windowSize = windowSize;
|
||||||
|
position = new Position();
|
||||||
|
focusObject = Optional.empty();
|
||||||
|
calculateViewBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateViewBounds() {
|
||||||
|
viewBounds = new Rectangle((int) position.getX(), (int) position.getY(), windowSize.getWidth(), windowSize.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void focusOn(Player player) {
|
||||||
|
focusObject = Optional.of(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInView(Player player) {
|
||||||
|
return viewBounds.intersects(player.getX(), player.getY(), player.getWidth(), player.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getWindowSize() {
|
||||||
|
return windowSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(Position position) {
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(State state) {
|
||||||
|
if (focusObject.isPresent()) {
|
||||||
|
Position objectPosition = focusObject.get().getPosition();
|
||||||
|
position.setX(objectPosition.getX() - windowSize.getWidth() / 2);
|
||||||
|
position.setY(objectPosition.getY() - windowSize.getHeight() / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/display/Renderer.java
Normal file
86
src/display/Renderer.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package display;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import entity.creature.Player;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.PlayState;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Renderer {
|
||||||
|
public void render(Graphics2D g, State state) {
|
||||||
|
renderGameMap(g, state);
|
||||||
|
renderGameObjects(g, state);
|
||||||
|
renderUI(g, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderGameMap(Graphics2D g, State state) {
|
||||||
|
GameMap gameMap = state.getGameMap();
|
||||||
|
List<BufferedImage> tileset = gameMap.getTileSet();
|
||||||
|
Size stageSize = gameMap.getStageSize();
|
||||||
|
GameCamera camera = state.getGameCamera();
|
||||||
|
for (int i = 0; i < GameMap.NUMBER_OF_MAP_LAYER - 1; i++) {
|
||||||
|
for (int y = 0; y < stageSize.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < stageSize.getWidth(); x++) {
|
||||||
|
int id = gameMap.getMapDataCell(x, y, i);
|
||||||
|
if (id >= 1) {
|
||||||
|
Size tileSize = gameMap.getTileSize();
|
||||||
|
int scale = gameMap.getScale();
|
||||||
|
Position cp = camera.getPosition();
|
||||||
|
g.drawImage(
|
||||||
|
tileset.get(id - 1),
|
||||||
|
x * tileSize.getWidth() * scale - (int) cp.getX(), y * tileSize.getHeight() * scale - (int) cp.getY(),
|
||||||
|
tileSize.getWidth() * scale, tileSize.getHeight() * scale,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderGameObjects(Graphics2D g, State state) {
|
||||||
|
GameMap gameMap = state.getGameMap();
|
||||||
|
if (state instanceof PlayState playState) {
|
||||||
|
GameCamera camera = state.getGameCamera();
|
||||||
|
Position cp = camera.getPosition();
|
||||||
|
Player player = playState.getPlayer();
|
||||||
|
playState.getEntities().stream().forEach(entity -> {
|
||||||
|
g.drawImage(
|
||||||
|
entity.getCurrentImage(),
|
||||||
|
(int) entity.getX() - (int) cp.getX(),
|
||||||
|
(int) entity.getY() - (int) cp.getY(),
|
||||||
|
gameMap.getTileSize().getWidth() * gameMap.getScale(),
|
||||||
|
gameMap.getTileSize().getHeight() * gameMap.getScale(),
|
||||||
|
null);
|
||||||
|
});
|
||||||
|
g.drawImage(
|
||||||
|
player.getCurrentImage(),
|
||||||
|
(int) player.getX() - (int) cp.getX(),
|
||||||
|
(int) player.getY() - (int) cp.getY(),
|
||||||
|
gameMap.getTileSize().getWidth() * gameMap.getScale(),
|
||||||
|
gameMap.getTileSize().getHeight() * gameMap.getScale(),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderUI(Graphics2D g, State state) {
|
||||||
|
state.getUiContainers().forEach(uiContainer ->
|
||||||
|
g.drawImage(
|
||||||
|
uiContainer.getSprite(),
|
||||||
|
(int)uiContainer.getRelativePosition().getX(),
|
||||||
|
(int)uiContainer.getRelativePosition().getY(),
|
||||||
|
null));
|
||||||
|
if (Display.debug) {
|
||||||
|
state.getUiDebugContainers().forEach(uiContainer ->
|
||||||
|
g.drawImage(
|
||||||
|
uiContainer.getSprite(),
|
||||||
|
(int)uiContainer.getRelativePosition().getX(),
|
||||||
|
(int)uiContainer.getRelativePosition().getY(),
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
199
src/entity/Entity.java
Normal file
199
src/entity/Entity.java
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package entity;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import controller.EnemyController;
|
||||||
|
import controller.EntityController;
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import core.Vector2D;
|
||||||
|
import entity.creature.Direction;
|
||||||
|
import gfx.Animation;
|
||||||
|
import gfx.SpriteSheet;
|
||||||
|
import input.Keyboard;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.PlayState;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public abstract class Entity {
|
||||||
|
private static final int DISTANCE_TO_ENTITY = 100;
|
||||||
|
|
||||||
|
protected GameMap gameMap;
|
||||||
|
protected Position position;
|
||||||
|
protected Size size;
|
||||||
|
protected Vector2D vector;
|
||||||
|
protected Keyboard keyboard;
|
||||||
|
protected Map<Direction, Animation> animations;
|
||||||
|
protected Animation currentAnimation;
|
||||||
|
protected Direction direction;
|
||||||
|
protected Rectangle collisionBox;
|
||||||
|
protected double walkSpeed;
|
||||||
|
protected EntityController controller;
|
||||||
|
|
||||||
|
public Entity(GameMap gameMap, Keyboard keyboard, Position position, Size size, String fileName, int tileWidth, int tileHeight) {
|
||||||
|
this.gameMap = gameMap;
|
||||||
|
this.position = position;
|
||||||
|
this.size = size;
|
||||||
|
this.keyboard = keyboard;
|
||||||
|
vector = new Vector2D();
|
||||||
|
SpriteSheet spriteSheet = new SpriteSheet(fileName, tileWidth, tileHeight);
|
||||||
|
registerAnimations(spriteSheet);
|
||||||
|
currentAnimation = animations.get(Direction.DOWN);
|
||||||
|
direction = Direction.STOP;
|
||||||
|
collisionBox = new Rectangle((int)position.getX(), (int)position.getY(),
|
||||||
|
size.getWidth() * gameMap.getScale(), size.getHeight() * gameMap.getScale());
|
||||||
|
walkSpeed = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void changeAnimation(Direction direction) {
|
||||||
|
currentAnimation.start();
|
||||||
|
if (!currentAnimation.equals(animations.get(direction))) {
|
||||||
|
currentAnimation = animations.get(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getCurrentImage() {
|
||||||
|
return currentAnimation.getCurrentImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getDirection() {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Direction, Animation> getAnimations() {
|
||||||
|
return animations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Animation getCurrentAnimation() {
|
||||||
|
return currentAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle getCollisionBox() {
|
||||||
|
return collisionBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard getKeyboard() {
|
||||||
|
return keyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return size.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2D getVector() {
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return size.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() {
|
||||||
|
return position.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getY() {
|
||||||
|
return position.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAnimation() {
|
||||||
|
switch (direction) {
|
||||||
|
case UP:
|
||||||
|
changeAnimation(Direction.UP);
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
changeAnimation(Direction.LEFT);
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
changeAnimation(Direction.DOWN);
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
changeAnimation(Direction.RIGHT);
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
currentAnimation.stop();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentAnimation.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleCollision(State state) {
|
||||||
|
GameMap map = state.getGameMap();
|
||||||
|
collisionBox.x += vector.getX();
|
||||||
|
collisionBox.y += vector.getY();
|
||||||
|
switch (direction) {
|
||||||
|
case UP:
|
||||||
|
vector.setVector(0, -walkSpeed);
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
vector.setVector(-walkSpeed, 0);
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
vector.setVector(0, walkSpeed);
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
vector.setVector(walkSpeed, 0);
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
default:
|
||||||
|
vector.setVector(0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!map.isWalkable(this) || !isWalkable(state)) {
|
||||||
|
vector.setVector(0, 0);
|
||||||
|
}
|
||||||
|
position.add(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCollision(Entity entity) {
|
||||||
|
Rectangle rect = new Rectangle(
|
||||||
|
collisionBox.x + (int)vector.getX(), collisionBox.y + (int)vector.getY(),
|
||||||
|
collisionBox.width, collisionBox.height);
|
||||||
|
return entity.getCollisionBox().intersects(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWalkable() {
|
||||||
|
return (vector.getX() != 0 || vector.getY() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isWalkable(State state) {
|
||||||
|
if (state instanceof PlayState playState) {
|
||||||
|
List<Entity> filterList = playState.getEntities().stream()
|
||||||
|
.filter(entity -> Math.abs(getX() - entity.getX()) < DISTANCE_TO_ENTITY && Math.abs(getY() - entity.getY()) < DISTANCE_TO_ENTITY)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (int i = 0; i < filterList.size(); i++) {
|
||||||
|
if (isCollision(filterList.get(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void registerAnimations(SpriteSheet spriteSheet) {
|
||||||
|
animations = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(State state) {
|
||||||
|
handleAnimation();
|
||||||
|
handleCollision(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/entity/creature/Creature.java
Normal file
138
src/entity/creature/Creature.java
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package entity.creature;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import entity.Entity;
|
||||||
|
import input.Keyboard;
|
||||||
|
import map.GameMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NESS LEVEL UP MAP
|
||||||
|
* LV | NEED EXP
|
||||||
|
* 1 | 0
|
||||||
|
* 2 | 4
|
||||||
|
* 3 | 17
|
||||||
|
* 4 | 44
|
||||||
|
* 5 | 109
|
||||||
|
* 6 | 236
|
||||||
|
* 7 | 449
|
||||||
|
* 8 | 772
|
||||||
|
* 9 | 1,229
|
||||||
|
* 10 | 1,884
|
||||||
|
* 11 | 2,611
|
||||||
|
* 12 | 3,644
|
||||||
|
* 13 | 4,877
|
||||||
|
* 14 | 6,364
|
||||||
|
* 15 | 8,129
|
||||||
|
* 16 | 10,703
|
||||||
|
* 17 | 13,241
|
||||||
|
* 18 | 16,214
|
||||||
|
* 19 | 19,673
|
||||||
|
* 20 | 23,699
|
||||||
|
* 21 | 28,253
|
||||||
|
* 22 | 33,476
|
||||||
|
* 23 | 39,389
|
||||||
|
* 24 | 46,043
|
||||||
|
* 25 | 53,489
|
||||||
|
* 26 | 61,778
|
||||||
|
* 27 | 70,961
|
||||||
|
* 28 | 81,089
|
||||||
|
* 29 | 92,213
|
||||||
|
* 30 | 104,384
|
||||||
|
* 31 | 117,653
|
||||||
|
* 32 | 132,071
|
||||||
|
* 33 | 147,689
|
||||||
|
* 34 | 164,558
|
||||||
|
* 35 | 182,729
|
||||||
|
* 36 | 202,270
|
||||||
|
* 37 | 223,249
|
||||||
|
* 38 | 245,734
|
||||||
|
* 39 | 269,793
|
||||||
|
* 40 | 295,494
|
||||||
|
* 41 | 322,905
|
||||||
|
* 42 | 352,094
|
||||||
|
* 43 | 383,129
|
||||||
|
* 44 | 416,078
|
||||||
|
* 45 | 451,009
|
||||||
|
* 46 | 487,990
|
||||||
|
* 47 | 527,089
|
||||||
|
* 48 | 568,374
|
||||||
|
* 49 | 611,913
|
||||||
|
* 50 | 657,774
|
||||||
|
* 51 | 706,025
|
||||||
|
* 52 | 756,734
|
||||||
|
* 53 | 809,969
|
||||||
|
* 54 | 865,798
|
||||||
|
* 55 | 924,289
|
||||||
|
* 56 | 985,510
|
||||||
|
* 57 | 1,049,529
|
||||||
|
* 58 | 1,116,414
|
||||||
|
* 59 | 1,186,233
|
||||||
|
* 60 | 1,259,054
|
||||||
|
* 61 | 1,335,030
|
||||||
|
* 62 | 1,414,314
|
||||||
|
* 63 | 1,497,059
|
||||||
|
* 64 | 1,583,418
|
||||||
|
* 65 | 1,673,544
|
||||||
|
* 66 | 1,767,590
|
||||||
|
* 67 | 1,865,709
|
||||||
|
* 68 | 1,968,054
|
||||||
|
* 69 | 2,074,778
|
||||||
|
* 70 | 2,186,034
|
||||||
|
* 71 | 2,301,975
|
||||||
|
* 72 | 2,422,754
|
||||||
|
* 73 | 2,548,524
|
||||||
|
* 74 | 2,679,438
|
||||||
|
* 75 | 2,815,649
|
||||||
|
* 76 | 2,957,310
|
||||||
|
* 77 | 3,104,574
|
||||||
|
* 78 | 3,257,594
|
||||||
|
* 79 | 3,416,523
|
||||||
|
* 80 | 3,581,514
|
||||||
|
* 81 | 3,752,720
|
||||||
|
* 82 | 3,930,294
|
||||||
|
* 83 | 4,114,389
|
||||||
|
* 84 | 4,305,158
|
||||||
|
* 85 | 4,502,754
|
||||||
|
* 86 | 4,707,330
|
||||||
|
* 87 | 4,919,039
|
||||||
|
* 88 | 5,138,034
|
||||||
|
* 89 | 5,364,468
|
||||||
|
* 90 | 5,598,494
|
||||||
|
* 91 | 5,840,265
|
||||||
|
* 92 | 6,089,934
|
||||||
|
* 93 | 6,347,654
|
||||||
|
* 94 | 6,613,578
|
||||||
|
* 95 | 6,887,859
|
||||||
|
* 96 | 7,170,650
|
||||||
|
* 97 | 7,462,104
|
||||||
|
* 98 | 7,762,374
|
||||||
|
* 99 | 8,071,613
|
||||||
|
*/
|
||||||
|
public abstract class Creature extends Entity {
|
||||||
|
protected String name;
|
||||||
|
protected int level;
|
||||||
|
protected int maxhp;
|
||||||
|
protected int maxmp;
|
||||||
|
protected int hp;
|
||||||
|
protected int mp;
|
||||||
|
protected int exp;
|
||||||
|
protected int nextlevel;
|
||||||
|
protected int offens;
|
||||||
|
protected int defens;
|
||||||
|
protected int speed;
|
||||||
|
protected int guts;
|
||||||
|
protected int vitality;
|
||||||
|
protected int iq;
|
||||||
|
protected int luck;
|
||||||
|
// TODO protected List<Magic> psis;
|
||||||
|
|
||||||
|
public Creature(GameMap gameMap, String name, Keyboard keyboard, Position position, Size size, String fileName, int tileWidth, int tileHeight) {
|
||||||
|
super(gameMap, keyboard, position, size, fileName, tileWidth, tileHeight);
|
||||||
|
this.name = name;
|
||||||
|
this.level = 1;
|
||||||
|
determiningStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void determiningStatus();
|
||||||
|
}
|
||||||
9
src/entity/creature/Direction.java
Normal file
9
src/entity/creature/Direction.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package entity.creature;
|
||||||
|
|
||||||
|
public enum Direction {
|
||||||
|
STOP,
|
||||||
|
UP,
|
||||||
|
DOWN,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
}
|
||||||
79
src/entity/creature/Enemy.java
Normal file
79
src/entity/creature/Enemy.java
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package entity.creature;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
|
||||||
|
import ai.AIManager;
|
||||||
|
import controller.EnemyController;
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import display.DebugRenderer;
|
||||||
|
import entity.creature.Direction;
|
||||||
|
import game.GameLoop;
|
||||||
|
import gfx.Animation;
|
||||||
|
import gfx.SpriteSheet;
|
||||||
|
import input.Keyboard;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Enemy extends Creature {
|
||||||
|
private AIManager aiManager;
|
||||||
|
|
||||||
|
public Enemy(GameMap gameMap, String name, Keyboard keyboard, Position position, Size size, String fileName, int tileWidth, int tileHeight) {
|
||||||
|
super(gameMap, name, keyboard, position, size, fileName, tileWidth, tileHeight);
|
||||||
|
aiManager = new AIManager();
|
||||||
|
collisionBox = new Rectangle((int)position.getX() + 25, (int)position.getY() + 25, 4 * gameMap.getScale(), 8 * gameMap.getScale());
|
||||||
|
controller = new EnemyController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void determiningStatus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentState() {
|
||||||
|
return aiManager.getCurrentAIState().getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleCollision(State state) {
|
||||||
|
GameMap map = state.getGameMap();
|
||||||
|
collisionBox.x += vector.getX();
|
||||||
|
collisionBox.y += vector.getY();
|
||||||
|
vector.setVector(0, 0);
|
||||||
|
if (controller.isRequestKeyUp()) {
|
||||||
|
changeAnimation(Direction.UP);
|
||||||
|
vector.setVector(0, -walkSpeed);
|
||||||
|
}
|
||||||
|
if (controller.isRequestKeyDown()) {
|
||||||
|
changeAnimation(Direction.DOWN);
|
||||||
|
vector.setVector(0, walkSpeed);
|
||||||
|
}
|
||||||
|
if (controller.isRequestKeyLeft()) {
|
||||||
|
changeAnimation(Direction.LEFT);
|
||||||
|
vector.setVector(-walkSpeed, 0);
|
||||||
|
}
|
||||||
|
if (controller.isRequestKeyRight()) {
|
||||||
|
changeAnimation(Direction.RIGHT);
|
||||||
|
vector.setVector(walkSpeed, 0);
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// if (!map.isWalkable(this) || !isWalkable(state)) {
|
||||||
|
// vector.setVector(0, 0);
|
||||||
|
// }
|
||||||
|
position.add(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerAnimations(SpriteSheet spriteSheet) {
|
||||||
|
super.registerAnimations(spriteSheet);
|
||||||
|
animations.put(Direction.DOWN , new Animation(spriteSheet.getTextures(0, 4 ), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.RIGHT, new Animation(spriteSheet.getTextures(4, 8 ), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.LEFT , new Animation(spriteSheet.getTextures(8, 12), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.UP , new Animation(spriteSheet.getTextures(12, 16), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
super.update(state);
|
||||||
|
aiManager.update(state, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/entity/creature/Player.java
Normal file
68
src/entity/creature/Player.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package entity.creature;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import core.Vector2D;
|
||||||
|
import game.GameLoop;
|
||||||
|
import gfx.Animation;
|
||||||
|
import gfx.SpriteSheet;
|
||||||
|
import input.Keyboard;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Player extends Creature {
|
||||||
|
public Player(GameMap gameMap, String name, Keyboard keyboard, Position position, Size size, String fileName, int tileWidth, int tileHeight) {
|
||||||
|
super(gameMap, name, keyboard, position, size, fileName, tileWidth, tileHeight);
|
||||||
|
collisionBox = new Rectangle((int)position.getX() + 25, (int)position.getY() + 25, 4 * gameMap.getScale(), 8 * gameMap.getScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void determiningStatus() {
|
||||||
|
maxhp = 30;
|
||||||
|
maxmp = 10;
|
||||||
|
hp = maxhp;
|
||||||
|
mp = maxmp;
|
||||||
|
// TODO nextlevel;
|
||||||
|
offens = 2;
|
||||||
|
defens = 2;
|
||||||
|
speed = 2;
|
||||||
|
guts = 2;
|
||||||
|
vitality = 2;
|
||||||
|
iq = 2;
|
||||||
|
luck = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleInput() {
|
||||||
|
if (keyboard.isPressedKey(KeyEvent.VK_W)) {
|
||||||
|
direction = Direction.UP;
|
||||||
|
} else if (keyboard.isPressedKey(KeyEvent.VK_A)) {
|
||||||
|
direction = Direction.LEFT;
|
||||||
|
} else if (keyboard.isPressedKey(KeyEvent.VK_S)) {
|
||||||
|
direction = Direction.DOWN;
|
||||||
|
} else if (keyboard.isPressedKey(KeyEvent.VK_D)) {
|
||||||
|
direction = Direction.RIGHT;
|
||||||
|
} else {
|
||||||
|
direction = Direction.STOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerAnimations(SpriteSheet spriteSheet) {
|
||||||
|
super.registerAnimations(spriteSheet);
|
||||||
|
animations.put(Direction.DOWN , new Animation(spriteSheet.getTextures(4, 7 ), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.RIGHT, new Animation(spriteSheet.getTextures(48, 51), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.LEFT , new Animation(spriteSheet.getTextures(63, 66), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
animations.put(Direction.UP , new Animation(spriteSheet.getTextures(19, 22), (long) (0.3 * GameLoop.ups), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
super.update(state);
|
||||||
|
handleInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/game/Game.java
Normal file
57
src/game/Game.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package game;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import display.Display;
|
||||||
|
import input.Keyboard;
|
||||||
|
import input.Mouse;
|
||||||
|
import state.StartState;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class Game {
|
||||||
|
private GameLoop gameLoop;
|
||||||
|
private Display display;
|
||||||
|
private Keyboard keyboard;
|
||||||
|
private Mouse mouse;
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
public Game(GameLoop gameLoop) {
|
||||||
|
this.gameLoop = gameLoop;
|
||||||
|
keyboard = new Keyboard();
|
||||||
|
mouse = new Mouse();
|
||||||
|
display = new Display(keyboard, mouse, "2D Game Engine Example", new Size(1280, 720));
|
||||||
|
state = new StartState(display.getCanvasSize(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Display getDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard getKeyboard() {
|
||||||
|
return keyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mouse getMouse() {
|
||||||
|
return mouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render() {
|
||||||
|
display.render(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
gameLoop.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
if (keyboard.isJustPressedKey(KeyEvent.VK_ESCAPE)) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
state.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/game/GameLoop.java
Normal file
67
src/game/GameLoop.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package game;
|
||||||
|
|
||||||
|
public class GameLoop implements Runnable {
|
||||||
|
public static int fpsCount = 0;
|
||||||
|
public static int upsCount = 0;
|
||||||
|
public static long loopCount = 0;
|
||||||
|
public static double fps = 30.0;
|
||||||
|
public static double ups = 30.0;
|
||||||
|
private boolean running;
|
||||||
|
private double targetUpsFrame = 1000000000.0 / ups;
|
||||||
|
private double targetFpsFrame = 1000000000.0 / fps;
|
||||||
|
private Game game;
|
||||||
|
|
||||||
|
public GameLoop() {
|
||||||
|
running = true;
|
||||||
|
game = new Game(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void render() {
|
||||||
|
game.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int fpsCnt = 0;
|
||||||
|
double fpsDelta = 0;
|
||||||
|
long timer = 0;
|
||||||
|
long last = System.nanoTime();
|
||||||
|
long loopCnt = 0;
|
||||||
|
int upsCnt = 0;
|
||||||
|
double upsDelta = 0;
|
||||||
|
while (running) {
|
||||||
|
long now = System.nanoTime();
|
||||||
|
upsDelta += (now - last) / targetUpsFrame;
|
||||||
|
fpsDelta += (now - last) / targetFpsFrame;
|
||||||
|
timer += now - last;
|
||||||
|
last = now;
|
||||||
|
while (upsDelta >= 1) {
|
||||||
|
update();
|
||||||
|
upsDelta--;
|
||||||
|
upsCnt++;
|
||||||
|
}
|
||||||
|
while (fpsDelta >= 1) {
|
||||||
|
render();
|
||||||
|
fpsDelta--;
|
||||||
|
fpsCnt++;
|
||||||
|
}
|
||||||
|
loopCnt++;
|
||||||
|
if (timer >= 1000000000) {
|
||||||
|
fpsCount = fpsCnt;
|
||||||
|
upsCount = upsCnt;
|
||||||
|
loopCount = loopCnt;
|
||||||
|
timer = fpsCnt = upsCnt = 0;
|
||||||
|
loopCnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
game.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/gfx/Animation.java
Normal file
53
src/gfx/Animation.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package gfx;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Animation {
|
||||||
|
private BufferedImage currentImage;
|
||||||
|
private List<BufferedImage> images;
|
||||||
|
private long currentFrame;
|
||||||
|
private long nextFrame;
|
||||||
|
private int idx;
|
||||||
|
private boolean loop;
|
||||||
|
private boolean running;
|
||||||
|
|
||||||
|
public Animation(List<BufferedImage> images, long nextFrame, boolean loop) {
|
||||||
|
this.images = images;
|
||||||
|
this.nextFrame = nextFrame;
|
||||||
|
this.loop = loop;
|
||||||
|
currentImage = images.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getCurrentImage() {
|
||||||
|
return currentImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
if (running) {
|
||||||
|
currentFrame++;
|
||||||
|
} else {
|
||||||
|
if (currentFrame != 0 && idx != 0) {
|
||||||
|
currentFrame = 0;
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentFrame == nextFrame) {
|
||||||
|
currentFrame = 0;
|
||||||
|
if (loop && idx == images.size() - 1) {
|
||||||
|
idx = 0;
|
||||||
|
} else {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentImage = images.get(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/gfx/SpriteSheet.java
Normal file
53
src/gfx/SpriteSheet.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package gfx;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import util.ImageUtil;
|
||||||
|
|
||||||
|
public class SpriteSheet {
|
||||||
|
private int tileWidth;
|
||||||
|
private int tileHeight;
|
||||||
|
private BufferedImage sheet;
|
||||||
|
private List<BufferedImage> sprites;
|
||||||
|
|
||||||
|
public SpriteSheet(String fileName, int tileWidth, int tileHeight) {
|
||||||
|
this.tileWidth = tileWidth;
|
||||||
|
this.tileHeight = tileHeight;
|
||||||
|
sprites = new ArrayList<>();
|
||||||
|
loadImage(fileName);
|
||||||
|
loadTextures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadImage(String fileName) {
|
||||||
|
try {
|
||||||
|
sheet = ImageUtil.loadImage(fileName);
|
||||||
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
|
throw new IllegalStateException("スプライトシートの読み込みに失敗しました。", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTextures() {
|
||||||
|
for (int y = 0; y < sheet.getHeight(null); y += tileHeight) {
|
||||||
|
for (int x = 0; x < sheet.getWidth(null); x += tileWidth) {
|
||||||
|
if (x + tileWidth <= sheet.getWidth() && y + tileHeight <= sheet.getHeight()) {
|
||||||
|
sprites.add(sheet.getSubimage(x, y, tileWidth, tileHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getSprite(int idx) {
|
||||||
|
return sprites.get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BufferedImage> getTextures(int start, int end) {
|
||||||
|
List<BufferedImage> textures = new ArrayList<>();
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
textures.add(getSprite(i));
|
||||||
|
}
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/input/Keyboard.java
Normal file
63
src/input/Keyboard.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package input;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
|
||||||
|
public class Keyboard implements KeyListener {
|
||||||
|
private boolean[] isKeyPressed;
|
||||||
|
private byte[] keyPressedTime;
|
||||||
|
|
||||||
|
public Keyboard() {
|
||||||
|
isKeyPressed = new boolean[0xFFFF];
|
||||||
|
keyPressedTime = new byte[0xFFFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
isKeyPressed[e.getKeyCode()] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
isKeyPressed[e.getKeyCode()] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeyPressTime(int keyCode) {
|
||||||
|
if (isKeyPressed[keyCode]) {
|
||||||
|
if (keyPressedTime[keyCode] < Byte.MAX_VALUE) {
|
||||||
|
keyPressedTime[keyCode]++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyPressedTime[keyCode] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJustPressedKey(int keyCode) {
|
||||||
|
return keyPressedTime[keyCode] == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPressedKey(int keyCode) {
|
||||||
|
return keyPressedTime[keyCode] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPressedKey(int... keyCodes) {
|
||||||
|
for (int keyCode : keyCodes) {
|
||||||
|
if (keyPressedTime[keyCode] == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
handleKeyPressTime(KeyEvent.VK_A);
|
||||||
|
handleKeyPressTime(KeyEvent.VK_D);
|
||||||
|
handleKeyPressTime(KeyEvent.VK_S);
|
||||||
|
handleKeyPressTime(KeyEvent.VK_W);
|
||||||
|
handleKeyPressTime(KeyEvent.VK_ESCAPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/input/Mouse.java
Normal file
78
src/input/Mouse.java
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package input;
|
||||||
|
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.awt.event.MouseMotionListener;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
|
||||||
|
public class Mouse implements MouseListener, MouseMotionListener {
|
||||||
|
private Position mousePosition;
|
||||||
|
private boolean mouseClicked;
|
||||||
|
private boolean mouseReleased;
|
||||||
|
private boolean mousePressed;
|
||||||
|
|
||||||
|
public Mouse() {
|
||||||
|
mousePosition = new Position(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanUpInputEvents() {
|
||||||
|
mouseClicked = false;
|
||||||
|
mouseReleased = false;
|
||||||
|
mousePressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getMousePosition() {
|
||||||
|
return mousePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMouseClicked() {
|
||||||
|
return mouseClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMouseReleased() {
|
||||||
|
return mouseReleased;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMousePressed() {
|
||||||
|
return mousePressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
mousePosition.setX(e.getPoint().getX());
|
||||||
|
mousePosition.setY(e.getPoint().getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseMoved(MouseEvent e) {
|
||||||
|
mousePosition.setX(e.getPoint().getX());
|
||||||
|
mousePosition.setY(e.getPoint().getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
mousePressed = e.getButton() == MouseEvent.BUTTON1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||||
|
mouseClicked = true;
|
||||||
|
mousePressed = false;
|
||||||
|
mouseReleased = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/input/MouseConsumer.java
Normal file
9
src/input/MouseConsumer.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package input;
|
||||||
|
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public interface MouseConsumer {
|
||||||
|
void onClick(State state);
|
||||||
|
void onDrag(State state);
|
||||||
|
void onRelease(State state);
|
||||||
|
}
|
||||||
213
src/map/GameMap.java
Normal file
213
src/map/GameMap.java
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
package map;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import core.Vector2D;
|
||||||
|
import entity.Entity;
|
||||||
|
import util.ImageUtil;
|
||||||
|
import util.MapUtil;
|
||||||
|
|
||||||
|
public class GameMap {
|
||||||
|
public static final int MAP_LAYER_1 = 0;
|
||||||
|
public static final int MAP_LAYER_2 = 1;
|
||||||
|
public static final int MAP_LAYER_3 = 2;
|
||||||
|
public static final int MAP_LAYER_4 = 3;
|
||||||
|
public static final int NUMBER_OF_MAP_LAYER = 4;
|
||||||
|
public static final int UNWALKABLE_TILE = 0;
|
||||||
|
public static final int WALKABLE_TILE = 1;
|
||||||
|
|
||||||
|
private List<int[][]> mapData;
|
||||||
|
private List<BufferedImage> tileset;
|
||||||
|
private String tileSetFileName;
|
||||||
|
private Size stageSize;
|
||||||
|
private Size tileSize;
|
||||||
|
private int scale;
|
||||||
|
private Rectangle tileRect;
|
||||||
|
private Rectangle tileRect2;
|
||||||
|
private Random random;
|
||||||
|
|
||||||
|
public GameMap() {
|
||||||
|
mapData = new ArrayList<>();
|
||||||
|
tileset = new ArrayList<>();
|
||||||
|
scale = 4;
|
||||||
|
tileRect = new Rectangle(-1, -1, 0, 0);
|
||||||
|
tileRect2 = new Rectangle(-1, -1, 0, 0);
|
||||||
|
random = new Random();
|
||||||
|
buildGameMap();
|
||||||
|
loadTileSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildGameMap() {
|
||||||
|
try {
|
||||||
|
Map<String, String> map = MapUtil.loadMapFromFileName("map1.tmx");
|
||||||
|
tileSetFileName = map.get(MapUtil.MAP_KEY_SOURCE);
|
||||||
|
for (int i = 1; i <= NUMBER_OF_MAP_LAYER; i++) {
|
||||||
|
// TODO 今IDは使われていない
|
||||||
|
int id = Integer.parseInt(map.get(MapUtil.MAP_KEY_ID + i));
|
||||||
|
int width = Integer.parseInt(map.get(MapUtil.MAP_KEY_WIDTH + i));
|
||||||
|
int height = Integer.parseInt(map.get(MapUtil.MAP_KEY_HEIGHT + i));
|
||||||
|
int tileWidth = Integer.parseInt(map.get(MapUtil.MAP_KEY_TILE_WIDTH));
|
||||||
|
int tileHeight = Integer.parseInt(map.get(MapUtil.MAP_KEY_TILE_HEIGHT));
|
||||||
|
stageSize = new Size(width, height);
|
||||||
|
tileSize = new Size(tileWidth, tileHeight);
|
||||||
|
String data = map.get(MapUtil.MAP_KEY_DATA + i);
|
||||||
|
int[][] arrData = new int[height][width];
|
||||||
|
String[] splitData = data.replace(" ", "").replace("\n", "").split(",");
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
arrData[y][x] = Integer.parseInt(splitData[y * width + x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapData.add(arrData);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("ゲームマップの構築に失敗しました。", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position findRandomPosition() {
|
||||||
|
return new Position(
|
||||||
|
random.nextInt(stageSize.getWidth() * (tileSize.getWidth() * scale)),
|
||||||
|
random.nextInt(stageSize.getHeight() * (tileSize.getHeight() * scale)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return stageSize.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int[][]> getMapData() {
|
||||||
|
return mapData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMapDataCell(int x, int y, int layer) {
|
||||||
|
return mapData.get(layer)[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
private Points getCollisionPoints(Entity entity) {
|
||||||
|
Vector2D vec = entity.getVector();
|
||||||
|
Rectangle rect = entity.getCollisionBox();
|
||||||
|
Points pos = new Points(-1);
|
||||||
|
switch (entity.getDirection()) {
|
||||||
|
case UP:
|
||||||
|
pos.p1.setX(rect.x);
|
||||||
|
pos.p1.setY(rect.y + vec.getY());
|
||||||
|
pos.p2.setX(rect.x + rect.width);
|
||||||
|
pos.p2.setY(rect.y + vec.getY());
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
pos.p1.setX(rect.x);
|
||||||
|
pos.p1.setY(rect.y + rect.height + vec.getY());
|
||||||
|
pos.p2.setX(rect.x + rect.width);
|
||||||
|
pos.p2.setY(rect.y + rect.height + vec.getY());
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
pos.p1.setX(rect.x + vec.getX());
|
||||||
|
pos.p1.setY(rect.y);
|
||||||
|
pos.p2.setX(rect.x + vec.getX());
|
||||||
|
pos.p2.setY(rect.y + rect.height);
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
pos.p1.setX(rect.x + rect.width + vec.getX());
|
||||||
|
pos.p1.setY(rect.y);
|
||||||
|
pos.p2.setX(rect.x + rect.width + vec.getX());
|
||||||
|
pos.p2.setY(rect.y + rect.height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle getTileRect() {
|
||||||
|
return tileRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle getTileRect2() {
|
||||||
|
return tileRect2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BufferedImage> getTileSet() {
|
||||||
|
return tileset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getStageSize() {
|
||||||
|
return stageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getTileSize() {
|
||||||
|
return tileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return stageSize.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOutBoundsOfMap(int x, int y) {
|
||||||
|
return x < 0 || x >= stageSize.getWidth() || y < 0 || y >= stageSize.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWalkable(Entity entity) {
|
||||||
|
Points pos = getCollisionPoints(entity);
|
||||||
|
double gridX1d = pos.p1.getX() / (tileSize.getWidth() * scale);
|
||||||
|
double gridY1d = pos.p1.getY() / (tileSize.getHeight() * scale);
|
||||||
|
double gridX2d = pos.p2.getX() / (tileSize.getWidth() * scale);
|
||||||
|
double gridY2d = pos.p2.getY() / (tileSize.getHeight() * scale);
|
||||||
|
int gridX1 = gridX1d < 0 ? -1 : (int)gridX1d;
|
||||||
|
int gridY1 = gridY1d < 0 ? -1 : (int)gridY1d;
|
||||||
|
int gridX2 = gridX2d < 0 ? -1 : (int)gridX2d;
|
||||||
|
int gridY2 = gridY2d < 0 ? -1 : (int)gridY2d;
|
||||||
|
if (isOutBoundsOfMap(gridX1, gridY1) && isOutBoundsOfMap(gridX2, gridY2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tileRect = new Rectangle(gridX1 * (tileSize.getWidth() * scale), gridY1 * (tileSize.getHeight() * scale), tileSize.getWidth() * scale, tileSize.getHeight() * scale);
|
||||||
|
tileRect2 = new Rectangle(gridX2 * (tileSize.getWidth() * scale), gridY2 * (tileSize.getHeight() * scale), tileSize.getWidth() * scale, tileSize.getHeight() * scale);
|
||||||
|
if (mapData.get(MAP_LAYER_4)[gridY1][gridX1] == UNWALKABLE_TILE ||
|
||||||
|
mapData.get(MAP_LAYER_4)[gridY2][gridX2] == UNWALKABLE_TILE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTileSet() {
|
||||||
|
try {
|
||||||
|
BufferedImage image = ImageUtil.loadImage(MapUtil.TILE_SET_PATH + tileSetFileName);
|
||||||
|
for (int y = 0; y < image.getHeight(null); y += tileSize.getHeight()) {
|
||||||
|
for (int x = 0; x < image.getWidth(null); x += tileSize.getWidth()) {
|
||||||
|
tileset.add(image.getSubimage(x, y, tileSize.getWidth(), tileSize.getHeight()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("指定されたパスから正しくタイルセットが読み込めませんでした。 path: " + MapUtil.TILE_SET_PATH + tileSetFileName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Points {
|
||||||
|
public Position p1;
|
||||||
|
public Position p2;
|
||||||
|
|
||||||
|
public Points() {
|
||||||
|
p1 = new Position();
|
||||||
|
p2 = new Position();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Points(int p) {
|
||||||
|
p1 = new Position(p, p);
|
||||||
|
p2 = new Position(p, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Points(int x1, int y1, int x2, int y2) {
|
||||||
|
p1 = new Position(x1, y1);
|
||||||
|
p2 = new Position(y1, y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/map/Tile.java
Normal file
44
src/map/Tile.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package map;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import gfx.Animation;
|
||||||
|
|
||||||
|
public class Tile {
|
||||||
|
private Animation animation;
|
||||||
|
private int tileWidth;
|
||||||
|
private int tileHeight;
|
||||||
|
private boolean walkable;
|
||||||
|
|
||||||
|
public Tile() {
|
||||||
|
walkable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tile(Animation animation, int tileWidth, int tileHeight, boolean walkable) {
|
||||||
|
this.animation = animation;
|
||||||
|
this.tileWidth = tileWidth;
|
||||||
|
this.tileHeight = tileHeight;
|
||||||
|
this.walkable = walkable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME 必要なければ削除
|
||||||
|
public static Tile copyOf(Tile tile) {
|
||||||
|
return new Tile(tile.getSprite(), tile.getTileIndex(), tile.getTileName(), tile.isWalkable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getSprite() {
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTileIndex() {
|
||||||
|
return tileIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTileName() {
|
||||||
|
return tileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWalkable() {
|
||||||
|
return walkable;
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/state/PlayState.java
Normal file
56
src/state/PlayState.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package state;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import display.Display;
|
||||||
|
import entity.Entity;
|
||||||
|
import entity.creature.Player;
|
||||||
|
import game.Game;
|
||||||
|
import game.GameLoop;
|
||||||
|
import ui.Alignment;
|
||||||
|
import ui.UIContainer;
|
||||||
|
import ui.VerticalContainer;
|
||||||
|
import ui.clickable.UIButton;
|
||||||
|
import ui.debug.UIDebugInfo;
|
||||||
|
|
||||||
|
public class PlayState extends State {
|
||||||
|
private Player player;
|
||||||
|
|
||||||
|
public PlayState(Size windowSize, Game game) {
|
||||||
|
super(windowSize, game);
|
||||||
|
player = new Player(
|
||||||
|
gameMap,
|
||||||
|
"Tester",
|
||||||
|
keyboard,
|
||||||
|
new Position(320, 192),
|
||||||
|
new Size(gameMap.getTileSize().getWidth(), gameMap.getTileSize().getHeight()),
|
||||||
|
"/character/Full_SpriteSheet.png",
|
||||||
|
16, 32);
|
||||||
|
generateEntity(10);
|
||||||
|
gameCamera.focusOn(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeUI(Size windowSize) {
|
||||||
|
UIContainer container = new VerticalContainer(windowSize);
|
||||||
|
container.setAlignment(Alignment.Position.END, Alignment.Position.START);
|
||||||
|
container.addUIComponent(new UIButton("デバッグ", () -> Display.debug = !Display.debug));
|
||||||
|
container.addUIComponent(new UIButton("終了", () -> game.stop()));
|
||||||
|
uiContainers.add(container);
|
||||||
|
UIDebugInfo uiDebugInfo = new UIDebugInfo(windowSize);
|
||||||
|
uiDebugContainers.add(uiDebugInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
super.update();
|
||||||
|
player.update(this);
|
||||||
|
entities.forEach(entity -> entity.update(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/state/StartState.java
Normal file
17
src/state/StartState.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package state;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import game.Game;
|
||||||
|
import ui.state.start.UIStartStateMenu;
|
||||||
|
|
||||||
|
public class StartState extends State {
|
||||||
|
public StartState(Size windowSize, Game game) {
|
||||||
|
super(windowSize, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeUI(Size windowSize) {
|
||||||
|
UIStartStateMenu menu = new UIStartStateMenu(windowSize, game);
|
||||||
|
uiContainers.add(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/state/State.java
Normal file
106
src/state/State.java
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package state;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import display.Display;
|
||||||
|
import display.GameCamera;
|
||||||
|
import entity.Entity;
|
||||||
|
import entity.creature.Enemy;
|
||||||
|
import game.Game;
|
||||||
|
import input.Keyboard;
|
||||||
|
import input.Mouse;
|
||||||
|
import input.MouseConsumer;
|
||||||
|
import map.GameMap;
|
||||||
|
import ui.UIContainer;
|
||||||
|
import ui.debug.UIDebugInfo;
|
||||||
|
|
||||||
|
public abstract class State {
|
||||||
|
protected Game game;
|
||||||
|
protected Keyboard keyboard;
|
||||||
|
protected Mouse mouse;
|
||||||
|
protected GameMap gameMap;
|
||||||
|
protected GameCamera gameCamera;
|
||||||
|
protected MouseConsumer activeConsumer;
|
||||||
|
protected Random random;
|
||||||
|
protected List<Entity> entities;
|
||||||
|
protected List<UIContainer> uiContainers;
|
||||||
|
protected List<UIContainer> uiDebugContainers;
|
||||||
|
protected UIDebugInfo uiDebugInfo;
|
||||||
|
|
||||||
|
public State(Size windowSize, Game game) {
|
||||||
|
this.game = game;
|
||||||
|
keyboard = game.getKeyboard();
|
||||||
|
mouse = game.getMouse();
|
||||||
|
gameMap = new GameMap();
|
||||||
|
gameCamera = new GameCamera(game.getDisplay().getCanvasSize());
|
||||||
|
random = new Random();
|
||||||
|
entities = new ArrayList<>();
|
||||||
|
uiContainers = new ArrayList<>();
|
||||||
|
uiDebugContainers = new ArrayList<>();
|
||||||
|
initializeUI(windowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateEntity(int num) {
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
int randomX = random.nextInt(gameMap.getWidth()) * gameMap.getTileSize().getWidth() * gameMap.getScale();
|
||||||
|
int randomY = random.nextInt(gameMap.getHeight()) * gameMap.getTileSize().getHeight() * gameMap.getScale();
|
||||||
|
entities.add(
|
||||||
|
new Enemy(
|
||||||
|
gameMap,
|
||||||
|
"Enemy" + i,
|
||||||
|
keyboard,
|
||||||
|
new Position(randomX, randomY),
|
||||||
|
new Size(gameMap.getTileSize().getWidth(), gameMap.getTileSize().getHeight()),
|
||||||
|
"/character/Full_SpriteSheet.png",
|
||||||
|
16, 16
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entity> getEntities() {
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameCamera getGameCamera() {
|
||||||
|
return gameCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameMap getGameMap() {
|
||||||
|
return gameMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mouse getMouse() {
|
||||||
|
return mouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UIContainer> getUiContainers() {
|
||||||
|
return uiContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UIContainer> getUiDebugContainers() {
|
||||||
|
return uiDebugContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void initializeUI(Size windowSize);
|
||||||
|
|
||||||
|
public void setActiveConsumer(MouseConsumer activeConsumer) {
|
||||||
|
this.activeConsumer = activeConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
keyboard.update();
|
||||||
|
entities.forEach(entity -> entity.update(this));
|
||||||
|
uiContainers.forEach(uiContainer -> uiContainer.update(this));
|
||||||
|
if (Display.debug) {
|
||||||
|
uiDebugContainers.forEach(uiContainer -> uiContainer.update(this));
|
||||||
|
}
|
||||||
|
gameCamera.update(this);
|
||||||
|
mouse.cleanUpInputEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/ui/Alignment.java
Normal file
25
src/ui/Alignment.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
public class Alignment {
|
||||||
|
public enum Position {
|
||||||
|
START,
|
||||||
|
CENTER,
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Position horizontal;
|
||||||
|
private final Position vertical;
|
||||||
|
|
||||||
|
public Alignment(Position horizontal, Position vertical) {
|
||||||
|
this.vertical = vertical;
|
||||||
|
this.horizontal = horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getHorizontal() {
|
||||||
|
return horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getVertical() {
|
||||||
|
return vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/ui/HorizontalContainer.java
Normal file
35
src/ui/HorizontalContainer.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
|
||||||
|
public class HorizontalContainer extends UIContainer {
|
||||||
|
public HorizontalContainer(Size windowSize) {
|
||||||
|
super(windowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void calculateChildrenPosition() {
|
||||||
|
int currentX = padding.getLeft();
|
||||||
|
for (UIComponent child : children) {
|
||||||
|
currentX += child.getMargin().getLeft();
|
||||||
|
child.setRelativePosition(new Position(currentX, padding.getTop()));
|
||||||
|
child.setAbsolutePosition(new Position(currentX + (int)absolutePosition.getX(), padding.getTop() + (int)absolutePosition.getY()));
|
||||||
|
currentX += child.getSize().getWidth();
|
||||||
|
currentX += child.getMargin().getRight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Size calculateChildrenSize() {
|
||||||
|
int combinedChildW = 0;
|
||||||
|
int tallestChildH = 0;
|
||||||
|
for (UIComponent child : children) {
|
||||||
|
combinedChildW += child.getSize().getWidth() + child.getMargin().getHorizontal();
|
||||||
|
if (child.getSize().getHeight() > tallestChildH) {
|
||||||
|
tallestChildH = child.getSize().getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Size(combinedChildW, tallestChildH);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/ui/Space.java
Normal file
47
src/ui/Space.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
public class Space {
|
||||||
|
private int top;
|
||||||
|
private int bottom;
|
||||||
|
private int left;
|
||||||
|
private int right;
|
||||||
|
|
||||||
|
public Space(int space) {
|
||||||
|
this(space, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Space(int horizontal, int vertical) {
|
||||||
|
this(vertical, vertical, horizontal, horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Space(int top, int bottom, int left, int right) {
|
||||||
|
this.top = top;
|
||||||
|
this.bottom = bottom;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBottom() {
|
||||||
|
return bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHorizontal() {
|
||||||
|
return left + right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLeft() {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRight() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTop() {
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVertical() {
|
||||||
|
return top + bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/ui/UIComponent.java
Normal file
67
src/ui/UIComponent.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public abstract class UIComponent {
|
||||||
|
protected Position relativePosition;
|
||||||
|
protected Position absolutePosition;
|
||||||
|
protected Size size;
|
||||||
|
protected Space margin;
|
||||||
|
protected Space padding;
|
||||||
|
|
||||||
|
public UIComponent() {
|
||||||
|
relativePosition = new Position(0, 0);
|
||||||
|
absolutePosition = new Position(0, 0);
|
||||||
|
size = new Size(1, 1);
|
||||||
|
margin = new Space(0);
|
||||||
|
padding = new Space(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Space getMargin() {
|
||||||
|
return margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Space getPadding() {
|
||||||
|
return padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getAbsolutePosition() {
|
||||||
|
return absolutePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getRelativePosition() {
|
||||||
|
return relativePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract BufferedImage getSprite();
|
||||||
|
|
||||||
|
public void setMargin(Space margin) {
|
||||||
|
this.margin = margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPadding(Space padding) {
|
||||||
|
this.padding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAbsolutePosition(Position absolutePosition) {
|
||||||
|
this.absolutePosition = absolutePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelativePosition(Position relativePosition) {
|
||||||
|
this.relativePosition = relativePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(Size size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void update(State state);
|
||||||
|
}
|
||||||
124
src/ui/UIContainer.java
Normal file
124
src/ui/UIContainer.java
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Transparency;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
import state.State;
|
||||||
|
import ui.Space;
|
||||||
|
import util.ImageUtil;
|
||||||
|
|
||||||
|
public abstract class UIContainer extends UIComponent {
|
||||||
|
protected Color backgroundColor;
|
||||||
|
protected List<UIComponent> children;
|
||||||
|
protected Size windowSize;
|
||||||
|
protected Alignment alignment;
|
||||||
|
protected Size fixedSize;
|
||||||
|
|
||||||
|
public UIContainer(Size windowSize) {
|
||||||
|
super();
|
||||||
|
this.windowSize = windowSize;
|
||||||
|
backgroundColor = new Color(20, 20, 20);
|
||||||
|
alignment = new Alignment(Alignment.Position.START, Alignment.Position.START);
|
||||||
|
margin = new Space(5);
|
||||||
|
padding = new Space(5);
|
||||||
|
children = new ArrayList<>();
|
||||||
|
calculateSize();
|
||||||
|
calculatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUIComponent(UIComponent uiComponent) {
|
||||||
|
children.add(uiComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUIComponentAll(UIComponent... uiComponents) {
|
||||||
|
for (UIComponent uiComponent : uiComponents) {
|
||||||
|
children.add(uiComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Size calculateChildrenSize();
|
||||||
|
|
||||||
|
protected abstract void calculateChildrenPosition();
|
||||||
|
|
||||||
|
private void calculatePosition() {
|
||||||
|
int x = padding.getLeft();
|
||||||
|
if (alignment.getHorizontal() == Alignment.Position.CENTER) {
|
||||||
|
x = windowSize.getWidth() / 2 - size.getWidth() / 2;
|
||||||
|
}
|
||||||
|
if (alignment.getHorizontal() == Alignment.Position.END) {
|
||||||
|
x = windowSize.getWidth() - size.getWidth() - margin.getRight();
|
||||||
|
}
|
||||||
|
int y = padding.getTop();
|
||||||
|
if (alignment.getVertical() == Alignment.Position.CENTER) {
|
||||||
|
y = windowSize.getHeight() / 2 - size.getHeight() / 2;
|
||||||
|
}
|
||||||
|
if (alignment.getVertical() == Alignment.Position.END) {
|
||||||
|
y = windowSize.getHeight() - size.getHeight() - margin.getBottom();
|
||||||
|
}
|
||||||
|
relativePosition = new Position(x, y);
|
||||||
|
absolutePosition = new Position(x, y);
|
||||||
|
calculateChildrenPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateSize() {
|
||||||
|
Size calculateChildrenSize = calculateChildrenSize();
|
||||||
|
if (fixedSize != null) {
|
||||||
|
size = fixedSize;
|
||||||
|
} else {
|
||||||
|
size = new Size(
|
||||||
|
padding.getHorizontal() + calculateChildrenSize.getWidth(),
|
||||||
|
padding.getVertical() + calculateChildrenSize.getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void clearChildren() {
|
||||||
|
children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getBackgroundColor() {
|
||||||
|
return backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedImage getSprite() {
|
||||||
|
BufferedImage image = ImageUtil.createCompatibleImage(size, Transparency.BITMASK);
|
||||||
|
Graphics2D g = image.createGraphics();
|
||||||
|
g.setColor(backgroundColor);
|
||||||
|
g.fillRect(0, 0, size.getWidth(), size.getHeight());
|
||||||
|
for (UIComponent child : children) {
|
||||||
|
g.drawImage(
|
||||||
|
child.getSprite(),
|
||||||
|
(int)child.getRelativePosition().getX(),
|
||||||
|
(int)child.getRelativePosition().getY(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
g.dispose();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlignment(Alignment.Position vertical, Alignment.Position horizontal) {
|
||||||
|
alignment = new Alignment(vertical, horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackgroundColor(Color backgroundColor) {
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFixedSize(Size fixedSize) {
|
||||||
|
this.fixedSize = fixedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
children.forEach(component -> component.update(state));
|
||||||
|
calculateSize();
|
||||||
|
calculatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/ui/UIText.java
Normal file
73
src/ui/UIText.java
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import java.awt.Canvas;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Transparency;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import state.State;
|
||||||
|
import util.ImageUtil;
|
||||||
|
|
||||||
|
public class UIText extends UIComponent {
|
||||||
|
private String text;
|
||||||
|
private int fontSize;
|
||||||
|
private int fontStyle;
|
||||||
|
private String fontFamily;
|
||||||
|
private Color color;
|
||||||
|
private boolean dropShadow;
|
||||||
|
private int dropShadowOffset;
|
||||||
|
private Color shadowColor;
|
||||||
|
private Font font;
|
||||||
|
|
||||||
|
public UIText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
fontSize = 24;
|
||||||
|
fontStyle = Font.BOLD;
|
||||||
|
fontFamily = Font.MONOSPACED;
|
||||||
|
color = Color.WHITE;
|
||||||
|
dropShadow = true;
|
||||||
|
dropShadowOffset = 2;
|
||||||
|
shadowColor = new Color(140, 140, 140);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFont() {
|
||||||
|
font = new Font(fontFamily, fontStyle, fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateSize() {
|
||||||
|
FontMetrics fontMetrics = new Canvas().getFontMetrics(font);
|
||||||
|
size = new Size(
|
||||||
|
fontMetrics.stringWidth(text) + padding.getHorizontal(),
|
||||||
|
fontMetrics.getHeight() + padding.getVertical()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedImage getSprite() {
|
||||||
|
BufferedImage image = (BufferedImage)ImageUtil.createCompatibleImage(size, Transparency.BITMASK);
|
||||||
|
Graphics2D g = image.createGraphics();
|
||||||
|
g.setFont(font);
|
||||||
|
if (dropShadow) {
|
||||||
|
g.setColor(shadowColor);
|
||||||
|
g.drawString(text, padding.getLeft() + dropShadowOffset, fontSize + padding.getTop() + dropShadowOffset);
|
||||||
|
}
|
||||||
|
g.setColor(color);
|
||||||
|
g.drawString(text, padding.getLeft(), fontSize + padding.getTop());
|
||||||
|
g.dispose();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
createFont();
|
||||||
|
calculateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/ui/VerticalContainer.java
Normal file
35
src/ui/VerticalContainer.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import core.Size;
|
||||||
|
|
||||||
|
public class VerticalContainer extends UIContainer {
|
||||||
|
public VerticalContainer(Size windowSize) {
|
||||||
|
super(windowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void calculateChildrenPosition() {
|
||||||
|
int currentY = padding.getTop();
|
||||||
|
for (UIComponent child : children) {
|
||||||
|
currentY += child.getMargin().getTop();
|
||||||
|
child.setRelativePosition(new Position(padding.getLeft(), currentY));
|
||||||
|
child.setAbsolutePosition(new Position(padding.getLeft() + (int)absolutePosition.getX(), currentY + (int)absolutePosition.getY()));
|
||||||
|
currentY += child.getSize().getHeight();
|
||||||
|
currentY += child.getMargin().getBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Size calculateChildrenSize() {
|
||||||
|
int combinedChildH = 0;
|
||||||
|
int widestChildW = 0;
|
||||||
|
for (UIComponent child : children) {
|
||||||
|
combinedChildH += child.getSize().getHeight() + child.getMargin().getVertical();
|
||||||
|
if (child.getSize().getWidth() > widestChildW) {
|
||||||
|
widestChildW = child.getSize().getWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Size(widestChildW, combinedChildH);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/ui/clickable/UIButton.java
Normal file
49
src/ui/clickable/UIButton.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package ui.clickable;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import ui.UIContainer;
|
||||||
|
import ui.UIText;
|
||||||
|
import ui.VerticalContainer;
|
||||||
|
import state.State;
|
||||||
|
|
||||||
|
public class UIButton extends UIClickable {
|
||||||
|
private UIContainer container;
|
||||||
|
private UIText label;
|
||||||
|
private Runnable clickEvent;
|
||||||
|
|
||||||
|
public UIButton(String label, Runnable clickEvent) {
|
||||||
|
this.label = new UIText(label);
|
||||||
|
this.clickEvent = clickEvent;
|
||||||
|
container = new VerticalContainer(new Size(0, 0));
|
||||||
|
container.addUIComponent(this.label);
|
||||||
|
container.setFixedSize(new Size(150, 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedImage getSprite() {
|
||||||
|
return container.getSprite();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
clickEvent.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
super.update(state);
|
||||||
|
container.update(state);
|
||||||
|
size = container.getSize();
|
||||||
|
Color color = Color.GRAY;
|
||||||
|
if (hasFocus) {
|
||||||
|
color = Color.LIGHT_GRAY;
|
||||||
|
}
|
||||||
|
if (isPressed) {
|
||||||
|
color = Color.DARK_GRAY;
|
||||||
|
}
|
||||||
|
container.setBackgroundColor(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/ui/clickable/UIClickable.java
Normal file
33
src/ui/clickable/UIClickable.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package ui.clickable;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
|
||||||
|
import core.Position;
|
||||||
|
import state.State;
|
||||||
|
import ui.UIComponent;
|
||||||
|
|
||||||
|
public abstract class UIClickable extends UIComponent {
|
||||||
|
protected boolean hasFocus;
|
||||||
|
protected boolean isPressed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(State state) {
|
||||||
|
Position mousePosition = state.getMouse().getMousePosition();
|
||||||
|
hasFocus = getBounds().contains((int)mousePosition.getX(), (int)mousePosition.getY());
|
||||||
|
isPressed = hasFocus && state.getMouse().isMousePressed();
|
||||||
|
if (hasFocus && state.getMouse().isMouseClicked()) {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rectangle getBounds() {
|
||||||
|
return new Rectangle(
|
||||||
|
(int)absolutePosition.getX(),
|
||||||
|
(int)absolutePosition.getY(),
|
||||||
|
size.getWidth(),
|
||||||
|
size.getHeight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void onClick();
|
||||||
|
}
|
||||||
48
src/ui/debug/UIDebugInfo.java
Normal file
48
src/ui/debug/UIDebugInfo.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package ui.debug;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import entity.creature.Player;
|
||||||
|
import game.GameLoop;
|
||||||
|
import map.GameMap;
|
||||||
|
import state.PlayState;
|
||||||
|
import state.State;
|
||||||
|
import ui.VerticalContainer;
|
||||||
|
import ui.UIText;
|
||||||
|
|
||||||
|
public class UIDebugInfo extends VerticalContainer {
|
||||||
|
private UIText ram = new UIText("RAM");
|
||||||
|
private UIText fps = new UIText("FPS");
|
||||||
|
private UIText ups = new UIText("UPS");
|
||||||
|
private UIText loop = new UIText("LOOP");
|
||||||
|
private UIText pos = new UIText("POS");
|
||||||
|
private UIText grid = new UIText("GRID");
|
||||||
|
private UIText dir = new UIText("DIR");
|
||||||
|
|
||||||
|
public UIDebugInfo(Size windowSize) {
|
||||||
|
super(windowSize);
|
||||||
|
setBackgroundColor(new Color(0, 0, 0, 0));
|
||||||
|
addUIComponentAll(ram, fps, ups, loop, pos, grid, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(State state) {
|
||||||
|
super.update(state);
|
||||||
|
ram.setText(String.format("RAM: %.3fMB",
|
||||||
|
(((double)(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000) / 1000)));
|
||||||
|
fps.setText(String.format("FPS: %d", GameLoop.fpsCount));
|
||||||
|
ups.setText(String.format("UPS: %d", GameLoop.upsCount));
|
||||||
|
loop.setText(String.format("1S : %d", GameLoop.loopCount));
|
||||||
|
if (state instanceof PlayState pState) {
|
||||||
|
Player p = pState.getPlayer();
|
||||||
|
GameMap gm = state.getGameMap();
|
||||||
|
Size tileSize = gm.getTileSize();
|
||||||
|
int scale = gm.getScale();
|
||||||
|
pos.setText(String.format("%s", "Player: " + p.getPosition()));
|
||||||
|
int gridX = ((int) p.getCollisionBox().getX() / (tileSize.getWidth() * scale));
|
||||||
|
int gridY = ((int) p.getCollisionBox().getY() / (tileSize.getHeight() * scale));
|
||||||
|
grid.setText(String.format("%s", "(X: " + gridX + " Y: " + gridY + ")"));
|
||||||
|
dir.setText(String.format("%s", p.getDirection()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/ui/state/start/UIStartStateMenu.java
Normal file
28
src/ui/state/start/UIStartStateMenu.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package ui.state.start;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
import game.Game;
|
||||||
|
import state.PlayState;
|
||||||
|
import ui.Alignment;
|
||||||
|
import ui.UIText;
|
||||||
|
import ui.VerticalContainer;
|
||||||
|
import ui.clickable.UIButton;
|
||||||
|
|
||||||
|
public class UIStartStateMenu extends VerticalContainer {
|
||||||
|
private Game game;
|
||||||
|
private UIText title;
|
||||||
|
private UIButton start;
|
||||||
|
private UIButton setting;
|
||||||
|
private UIButton exit;
|
||||||
|
|
||||||
|
public UIStartStateMenu(Size windowSize, Game game) {
|
||||||
|
super(windowSize);
|
||||||
|
this.game = game;
|
||||||
|
setAlignment(Alignment.Position.CENTER, Alignment.Position.CENTER);
|
||||||
|
title = new UIText("2D Game Engine Example");
|
||||||
|
start = new UIButton("開始", () -> game.setState(new PlayState(game.getDisplay().getCanvasSize(), game)));
|
||||||
|
setting = new UIButton("設定", () -> {});
|
||||||
|
exit = new UIButton("終了", () -> game.stop());
|
||||||
|
addUIComponentAll(title, start, setting, exit);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/util/ImageUtil.java
Normal file
36
src/util/ImageUtil.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Transparency;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import core.Size;
|
||||||
|
|
||||||
|
public class ImageUtil {
|
||||||
|
public static BufferedImage createCompatibleImage(Size size, int transparency) {
|
||||||
|
GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
|
return config.createCompatibleImage(size.getWidth(), size.getHeight(), transparency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage loadImage(String path) throws IOException, IllegalArgumentException {
|
||||||
|
try {
|
||||||
|
Image imageFromDisk = ImageIO.read(ImageUtil.class.getResource(path));
|
||||||
|
BufferedImage compatibleImage = createCompatibleImage(
|
||||||
|
new Size(imageFromDisk.getWidth(null), imageFromDisk.getHeight(null)), Transparency.BITMASK);
|
||||||
|
Graphics2D graphics = compatibleImage.createGraphics();
|
||||||
|
graphics.drawImage(imageFromDisk, 0, 0, null);
|
||||||
|
graphics.dispose();
|
||||||
|
return compatibleImage;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("指定されたパスからイメージを読み込む事が出来ませんでした。 path: " + path, e);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IOException("指定されたパスが存在しませんでした。 path: " + path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
src/util/MapUtil.java
Normal file
98
src/util/MapUtil.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
public class MapUtil {
|
||||||
|
public static final int NUMBER_OF_TILE_SIZE = 2;
|
||||||
|
public static final String MAP_KEY_DATA = "data";
|
||||||
|
public static final String MAP_KEY_HEIGHT = "height";
|
||||||
|
public static final String MAP_KEY_ID = "id";
|
||||||
|
public static final String MAP_KEY_WIDTH = "width";
|
||||||
|
public static final String MAP_KEY_SOURCE = "source";
|
||||||
|
public static final String MAP_KEY_TILE_HEIGHT = "tileheight";
|
||||||
|
public static final String MAP_KEY_TILE_WIDTH = "tilewidth";
|
||||||
|
public static final String TILE_SET_PATH = "/tileset/";
|
||||||
|
|
||||||
|
private static void loadMapInfo(Map<String, String> mapData, String fileName)
|
||||||
|
throws ParserConfigurationException, SAXException, IOException {
|
||||||
|
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||||
|
Document document = builder.parse(MapUtil.class.getResourceAsStream(TILE_SET_PATH + fileName));
|
||||||
|
document.getDocumentElement().normalize();
|
||||||
|
Element elementList = document.getDocumentElement();
|
||||||
|
NodeList nodeList = elementList.getElementsByTagName("tileset");
|
||||||
|
Element element = (Element) nodeList.item(0);
|
||||||
|
mapData.put(MAP_KEY_SOURCE, element.getAttribute(MAP_KEY_SOURCE).replace("tsx", "png"));
|
||||||
|
nodeList = elementList.getElementsByTagName("layer");
|
||||||
|
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||||
|
element = (Element) nodeList.item(i);
|
||||||
|
int id = Integer.parseInt(element.getAttribute(MAP_KEY_ID));
|
||||||
|
mapData.put(MAP_KEY_ID + id, element.getAttribute(MAP_KEY_ID));
|
||||||
|
mapData.put(MAP_KEY_WIDTH + id, element.getAttribute(MAP_KEY_WIDTH));
|
||||||
|
mapData.put(MAP_KEY_HEIGHT + id, element.getAttribute(MAP_KEY_HEIGHT));
|
||||||
|
mapData.put(MAP_KEY_DATA + id, element.getTextContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME TileSizeの値を取得する方法を考える必要がある。
|
||||||
|
private static void loadTileSizeInfo(Map<String, String> mapData, String fileName) throws IOException, URISyntaxException {
|
||||||
|
try (BufferedReader br = new BufferedReader(
|
||||||
|
new FileReader(new File(MapUtil.class.getResource(TILE_SET_PATH + fileName).toURI())))) {
|
||||||
|
String line = "";
|
||||||
|
int idx = 0;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
idx++;
|
||||||
|
if (idx == NUMBER_OF_TILE_SIZE) {
|
||||||
|
String beginStr = "tilewidth=";
|
||||||
|
String endStr = "tileheight=";
|
||||||
|
int beginIndex = line.indexOf(beginStr);
|
||||||
|
int endIndex = line.indexOf(endStr);
|
||||||
|
String tileWidth = line.substring(beginIndex, endIndex).replace(beginStr, "").replace("\"", "").replace(" ", "");
|
||||||
|
beginStr = "tileheight=";
|
||||||
|
endStr = "infinite=";
|
||||||
|
beginIndex = line.indexOf(beginStr);
|
||||||
|
endIndex = line.indexOf(endStr);
|
||||||
|
String tileHeight = line.substring(beginIndex, endIndex).replace(beginStr, "").replace("\"", "").replace(" ", "");
|
||||||
|
mapData.put(MAP_KEY_TILE_WIDTH, tileWidth);
|
||||||
|
mapData.put(MAP_KEY_TILE_HEIGHT, tileHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("タイルサイズの読み込みに失敗しました。 fileName: " + fileName, e);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
System.err.println("不正なURIが設定されたため例外が発生しました。 fileName: " + fileName);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> loadMapFromFileName(String fileName)
|
||||||
|
throws ParserConfigurationException, SAXException, IOException, URISyntaxException {
|
||||||
|
try {
|
||||||
|
Map<String, String> mapData = new HashMap<>();
|
||||||
|
loadMapInfo(mapData, fileName);
|
||||||
|
loadTileSizeInfo(mapData, fileName);
|
||||||
|
return mapData;
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
System.err.println("XMLの構成に致命的なミスがあります。 fileName: " + fileName);
|
||||||
|
throw e;
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new SAXException("SAXでの処理で例外が発生しました。fileName: " + fileName, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("入出力処理で例外が発生しました。 fileName: " + fileName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user