Last active
November 11, 2018 13:06
-
-
Save komamitsu/b470279b9577bb460655e990f3488f07 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mygdx.game; | |
import com.badlogic.gdx.ApplicationAdapter; | |
import com.badlogic.gdx.Gdx; | |
import com.badlogic.gdx.Input; | |
import com.badlogic.gdx.graphics.Camera; | |
import com.badlogic.gdx.graphics.GL20; | |
import com.badlogic.gdx.graphics.OrthographicCamera; | |
import com.badlogic.gdx.graphics.Texture; | |
import com.badlogic.gdx.graphics.g2d.BitmapFont; | |
import com.badlogic.gdx.graphics.g2d.SpriteBatch; | |
import com.badlogic.gdx.math.Rectangle; | |
import com.badlogic.gdx.scenes.scene2d.ui.Image; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Random; | |
import java.util.concurrent.CopyOnWriteArrayList; | |
public class MyGdxGame | |
extends ApplicationAdapter | |
{ | |
private Scene scene = new TitleScene(); | |
private static final int WINDOW_WIDTH = 800; | |
private static final int WINDOW_HEIGHT = 640; | |
@Override | |
public void create() | |
{ | |
Gdx.graphics.setWindowedMode(WINDOW_WIDTH, WINDOW_HEIGHT); | |
} | |
@Override | |
public void render() | |
{ | |
if (!scene.isInitialized()) { | |
scene.create(); | |
} | |
if (scene.render()) { | |
scene.dispose(); | |
if (scene instanceof TitleScene) { | |
scene = new GameScene(); | |
} | |
else if (scene instanceof GameScene) { | |
scene = new GameOverScene(); | |
} | |
else if (scene instanceof GameOverScene) { | |
scene = new TitleScene(); | |
} | |
} | |
} | |
@Override | |
public void dispose() | |
{ | |
scene.dispose(); | |
} | |
} | |
interface Scene | |
{ | |
void create(); | |
boolean isInitialized(); | |
boolean render(); | |
void dispose(); | |
} | |
class TitleScene | |
implements Scene | |
{ | |
private boolean initialized = false; | |
private SpriteBatch batch; | |
private BitmapFont font; | |
@Override | |
public void create() | |
{ | |
batch = new SpriteBatch(); | |
font = new BitmapFont(); | |
initialized = true; | |
} | |
@Override | |
public boolean isInitialized() | |
{ | |
return initialized; | |
} | |
@Override | |
public boolean render() | |
{ | |
Gdx.gl.glClearColor(128f / 256, 208f / 256, 48f / 256, 1); | |
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); | |
batch.begin(); | |
font.draw( | |
batch, | |
"Diep.io like Game\n-- Please press space key --", | |
Gdx.graphics.getWidth() / 2 - 50, | |
Gdx.graphics.getHeight() / 2); | |
batch.end(); | |
return Gdx.input.isKeyJustPressed(Input.Keys.SPACE); | |
} | |
@Override | |
public void dispose() | |
{ | |
batch.dispose(); | |
font.dispose(); | |
} | |
} | |
class GameScene | |
implements Scene | |
{ | |
private static final int SPEED = 5; | |
private static final int WINDOW_WIDTH = 800; | |
private static final int WINDOW_HEIGHT = 640; | |
private boolean initialized = false; | |
interface Damagable | |
{ | |
enum Result { | |
HIT, DESTROIED, NONE | |
} | |
Result onShot(int damage, Rectangle rect); | |
} | |
static class Player | |
{ | |
private static final Texture TEXTURE = new Texture("tank0.png"); | |
private static final long FIRE_INTERVAL_MS = 100; | |
private final Image image; | |
private final List<Bullet> bullets = new CopyOnWriteArrayList<Bullet>(); | |
private final Camera camera; | |
private long lastBulletFire = 0; | |
private int hp = 100; | |
Player(Camera camera, float x, float y) | |
{ | |
// プレイヤーとカメラは一緒に動くので、簡単のためにプレイヤーにカメラを持たせてしまう | |
// OnMovedCallbackとか経由でカメラを更新しても良いかも | |
this.camera = camera; | |
image = new Image(TEXTURE); | |
image.setPosition(x, y); | |
} | |
private void rotate(float moveX, float moveY) | |
{ | |
// float diffX = moveX; | |
// float diffY = moveY; | |
float diffX = Gdx.input.getX() - camera.viewportWidth / 2; | |
float diffY = -(Gdx.input.getY() - camera.viewportHeight / 2); | |
// 縦・横の移動量から回転する角度を計算 | |
double d = Math.toDegrees(Math.atan2(diffY, diffX)); | |
// 回転する軸を画像の中心に設定 | |
image.setOrigin( | |
image.getImageWidth() / 2, | |
image.getImageHeight() / 2); | |
// 回転 | |
image.setRotation((float) d); | |
} | |
private void updateCamera(float moveX, float moveY) | |
{ | |
// カメラの更新 | |
// カメラの位置をプレイヤーと同じ分だけ移動させる | |
camera.translate(moveX, moveY, 0); | |
// カメラの状態を更新 | |
camera.update(); | |
} | |
private Rectangle getRect() | |
{ | |
return new Rectangle(image.getX(), image.getY(), image.getWidth(), image.getHeight()); | |
} | |
void move(float moveX, float moveY, Blocks blocks) | |
{ | |
// 発射した弾の更新 | |
moveBullets(blocks); | |
// ブロックと衝突してたら反発 | |
if (blocks.conflicted(getRect())) { | |
moveX = -(moveX * 2); | |
moveY = -(moveY * 2); | |
// ダメージも受ける | |
hp -= 5; | |
} | |
// 画像の回転 | |
rotate(moveX, moveY); | |
// 場所を移動距離分更新 | |
image.setPosition( | |
image.getX() + moveX, | |
image.getY() + moveY); | |
// カメラの更新 | |
updateCamera(moveX, moveY); | |
} | |
private void moveBullets(Blocks blocks) | |
{ | |
for (Bullet bullet : bullets) { | |
bullet.move(); | |
bullet.hit(blocks); | |
if (bullet.finished()) { | |
bullets.remove(bullet); | |
} | |
} | |
} | |
void fire() | |
{ | |
long now = System.currentTimeMillis(); | |
// 前回の発射から一定時間経過していれば、発射可能 | |
if (lastBulletFire + FIRE_INTERVAL_MS < now) { | |
bullets.add( | |
new Bullet( | |
image.getX() + image.getOriginX(), | |
image.getY() + image.getOriginY(), | |
image.getRotation(), | |
0) | |
); | |
lastBulletFire = now; | |
} | |
} | |
void draw(SpriteBatch batch) | |
{ | |
image.draw(batch, 1); | |
for (Bullet bullet : bullets) { | |
bullet.draw(batch); | |
} | |
} | |
boolean isAlive() | |
{ | |
return hp > 0; | |
} | |
int getHp() | |
{ | |
return hp; | |
} | |
float getX() | |
{ | |
return image.getX(); | |
} | |
float getY() | |
{ | |
return image.getY(); | |
} | |
} | |
static class Bullet | |
{ | |
private static final Texture TEXTURE = new Texture("bullet.png"); | |
private static final Texture TEXTURE_CRASHING = new Texture("bullet_crashing.png"); | |
private static final int WIDTH = 32; | |
private static final int HEIGHT = 32; | |
private final Image image; | |
private final float angle; | |
private int distance; | |
private int crashingCount; | |
private boolean broken; | |
Bullet(float x, float y, float angle, int distance) | |
{ | |
image = new Image(TEXTURE); | |
image.setPosition(x - WIDTH / 2, y - HEIGHT / 2); | |
image.setWidth(WIDTH); | |
image.setHeight(HEIGHT); | |
this.angle = angle; | |
this.distance = distance; | |
} | |
void move() | |
{ | |
if (crashingCount > 0) { | |
crashingCount -= 1; | |
if (crashingCount == 0) { | |
broken = true; | |
return; | |
} | |
} | |
float moveX = (float) (10 * Math.cos(Math.toRadians(angle))); | |
float moveY = (float) (10 * Math.sin(Math.toRadians(angle))); | |
image.setPosition(image.getX() + moveX, image.getY() + moveY); | |
distance += 10; | |
} | |
boolean finished() | |
{ | |
// 一定距離を飛んだら終了 | |
return distance >= 800 || broken; | |
} | |
void draw(SpriteBatch batch) | |
{ | |
if (crashingCount > 0) { | |
batch.draw(TEXTURE_CRASHING, image.getX(), image.getY(), WIDTH, HEIGHT); | |
} | |
else { | |
image.draw(batch, 1); | |
} | |
} | |
void hit(Damagable damagable) | |
{ | |
if (crashingCount > 0 || broken) { | |
return; | |
} | |
Rectangle myRect = getRect(); | |
if (damagable.onShot(10, myRect) != Damagable.Result.NONE) { | |
crashingCount = 5; | |
} | |
} | |
private Rectangle getRect() | |
{ | |
return new Rectangle( | |
(int) image.getX(), | |
(int) image.getY(), | |
(int) image.getWidth(), | |
(int) image.getHeight()); | |
} | |
} | |
static class Block | |
implements Damagable | |
{ | |
private static final int SPEED = 3; | |
private static final List<Texture> TEXTURES = new ArrayList<Texture>(12); | |
private static final Texture TEXTURE_DAMAGED = new Texture("seiza_damaged.png"); | |
private int hp = 100; | |
private int inDamagedCount = 0; | |
private final Image image; | |
private final Image imageDamaged; | |
@Override | |
public Result onShot(int damage, Rectangle rect) | |
{ | |
if (getRect().overlaps(rect)) { | |
if (hp > 0) { | |
hp -= damage; | |
} | |
} | |
else { | |
return Result.NONE; | |
} | |
inDamagedCount = 10; | |
if (hp <= 0) { | |
return Result.DESTROIED; | |
} | |
else { | |
return Result.HIT; | |
} | |
} | |
static { | |
// 12個作成したいので12回ループ | |
for (int i = 0; i < 12; i++) { | |
// たまたま画像がseiza1.pngからseiza12.pngなので、ループカウントを画像ファイル名の指定に利用 | |
TEXTURES.add(new Texture("seiza" + (i + 1) + ".png")); | |
} | |
} | |
Block(int type, float x, float y) | |
{ | |
image = new Image(TEXTURES.get(type % 12)); | |
imageDamaged = new Image(TEXTURE_DAMAGED); | |
imageDamaged.setVisible(false); | |
image.setPosition(x, y); | |
} | |
void move(Player player) | |
{ | |
// 回転する軸を画像の中心に設定 | |
image.setOrigin(image.getImageWidth() / 2, image.getImageHeight() / 2); | |
// ちょっとずつ回転 | |
image.rotateBy(1); | |
if (player.getX() > image.getX()) { | |
image.setX(image.getX() + SPEED); | |
} | |
else if (player.getX() < image.getX()) { | |
image.setX(image.getX() - SPEED); | |
} | |
if (player.getY() > image.getY()) { | |
image.setY(image.getY() + SPEED); | |
} | |
else if (player.getY() < image.getY()) { | |
image.setY(image.getY() - SPEED); | |
} | |
// 回転する軸を画像の中心に設定 | |
image.setOrigin(image.getImageWidth() / 2, image.getImageHeight() / 2); | |
// ちょっとずつ回転 | |
image.rotateBy(1); | |
} | |
void draw(SpriteBatch batch) | |
{ | |
if (inDamagedCount > 0) { | |
image.setVisible(false); | |
imageDamaged.setX(image.getX()); | |
imageDamaged.setY(image.getY()); | |
imageDamaged.setOrigin(image.getOriginX(), image.getOriginY()); | |
imageDamaged.setRotation(image.getRotation()); | |
imageDamaged.setVisible(true); | |
imageDamaged.draw(batch, 1); | |
inDamagedCount -= 1; | |
} | |
else { | |
image.setVisible(true); | |
imageDamaged.setVisible(false); | |
image.draw(batch, 1); | |
} | |
} | |
Rectangle getRect() | |
{ | |
return new Rectangle( | |
(int) image.getX(), | |
(int) image.getY(), | |
(int) image.getWidth(), | |
(int) image.getHeight()); | |
} | |
} | |
static class Blocks | |
implements Damagable | |
{ | |
private final List<Block> blocks = new CopyOnWriteArrayList<Block>(); | |
Blocks(int count, float width, float height) | |
{ | |
// 適当なキャラクラーたちを複数作成 | |
Random random = new Random(); | |
for (int i = 0; i < count; i++) { | |
// 適当な位置に配置 | |
blocks.add(new Block(i, | |
random.nextInt((int) width) - width / 2, | |
random.nextInt((int) height) - height / 2)); | |
} | |
} | |
void move(Player player) | |
{ | |
for (Block block : blocks) { | |
block.move(player); | |
} | |
} | |
void draw(SpriteBatch batch) | |
{ | |
for (Block block : blocks) { | |
block.draw(batch); | |
} | |
} | |
boolean conflicted(Rectangle playerRect) | |
{ | |
// 全てのブロックの領域との衝突をチェック | |
for (Block block : blocks) { | |
Rectangle blockRect = block.getRect(); | |
if (playerRect.overlaps(blockRect)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
@Override | |
public Result onShot(int damage, Rectangle rect) | |
{ | |
for (Block block : blocks) { | |
switch (block.onShot(damage, rect)) { | |
case HIT: | |
return Result.HIT; | |
case DESTROIED: | |
blocks.remove(block); | |
return Result.DESTROIED; | |
case NONE: | |
break; | |
} | |
} | |
return Result.NONE; | |
} | |
} | |
static class Background | |
{ | |
private static final Texture TEXTURE = new Texture("bg.jpeg"); | |
void draw(SpriteBatch batch, float centerX, float centerY, float width, float height) | |
{ | |
// プレイヤーがいる画面を中心に、上下左右 3 x 3 画面の背景画像を描画 | |
// 背景画像はプレイヤーの細かい動きによらず、原点から画面幅・高さの倍数ごとの固定表示で | |
// プレイヤーが移動している感じを出している | |
for (int iY = 0; iY < 3; iY++) { | |
for (int iX = 0; iX < 3; iX++) { | |
batch.draw(TEXTURE, | |
(float) Math.ceil((((centerX - width / 2) + width * (iX - 1)) / width)) * width, | |
(float) Math.ceil((((centerY - height / 2) + height * (iY - 1)) / height)) * height, | |
width, height); | |
} | |
} | |
} | |
} | |
static class HitPoint | |
{ | |
private final BitmapFont font; | |
HitPoint(BitmapFont font) | |
{ | |
this.font = font; | |
} | |
void draw(SpriteBatch batch, Player player) | |
{ | |
font.draw( | |
batch, | |
String.format("HP: %d", player.getHp()), | |
player.getX() - 300, | |
player.getY() - 200); | |
} | |
} | |
private SpriteBatch batch; | |
private Player player; | |
private Blocks blocks; | |
private BitmapFont font; | |
private Background bg; | |
private HitPoint hp; | |
private Camera camera; | |
private Camera setupCamera(float width, float height) | |
{ | |
// カメラの設定 | |
OrthographicCamera camera = new OrthographicCamera(); | |
// 画面全体を表示 | |
camera.setToOrtho(false, width, height); | |
// 画面の中心にカメラを配置 | |
camera.position.set(camera.viewportWidth / 2f, camera.viewportHeight / 2f, 0); | |
// カメラの更新 | |
camera.update(); | |
return camera; | |
} | |
@Override | |
public void create() | |
{ | |
batch = new SpriteBatch(); | |
font = new BitmapFont(); | |
// 画面の縦と横 | |
float width = Gdx.graphics.getWidth(); | |
float height = Gdx.graphics.getHeight(); | |
// カメラの設定 | |
camera = setupCamera(width, height); | |
// プレイヤーを作成。開始位置を画面の中心にする | |
player = new Player(camera, width / 2, height / 2); | |
// 複数のブロックを作成 | |
blocks = new Blocks(36, width * 16, height * 16); | |
// 背景画像 | |
bg = new Background(); | |
// HP表示 | |
hp = new HitPoint(font); | |
initialized = true; | |
} | |
@Override | |
public boolean isInitialized() | |
{ | |
return initialized; | |
} | |
private void movePlayerWithInput() | |
{ | |
// プレイヤーの移動 | |
// 今回の描画でプレイヤーをどれだけ移動するかの値 | |
int moveX = 0; | |
int moveY = 0; | |
// キー入力に応じてプレイヤーの移動量を設定 | |
if (Gdx.input.isKeyPressed(Input.Keys.W)) { | |
moveY = SPEED; | |
} | |
if (Gdx.input.isKeyPressed(Input.Keys.S)) { | |
moveY = -SPEED; | |
} | |
if (Gdx.input.isKeyPressed(Input.Keys.D)) { | |
moveX = SPEED; | |
} | |
if (Gdx.input.isKeyPressed(Input.Keys.A)) { | |
moveX = -SPEED; | |
} | |
if (Gdx.input.isKeyPressed(Input.Keys.SPACE) || | |
Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { | |
player.fire(); | |
} | |
// プレイヤーと関連するものを移動 | |
player.move(moveX, moveY, blocks); | |
} | |
@Override | |
public boolean render() | |
{ | |
// 背景色の指定 | |
Gdx.gl.glClearColor(128f / 256, 208f / 256, 48f / 256, 1); | |
// 毎回全体をクリアしてる? | |
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); | |
// ゲームオーバーであれば何もしないで終わる | |
if (!player.isAlive()) { | |
return true; | |
} | |
movePlayerWithInput(); | |
blocks.move(player); | |
// スプライト画像等の描画 | |
// SpriteBatchとカメラを紐付け | |
batch.setProjectionMatrix(camera.combined); | |
// 描画開始 | |
batch.begin(); | |
bg.draw(batch, player.getX(), player.getY(), WINDOW_WIDTH, WINDOW_HEIGHT); | |
player.draw(batch); | |
blocks.draw(batch); | |
hp.draw(batch, player); | |
// 描画終了 | |
batch.end(); | |
return false; | |
} | |
@Override | |
public void dispose() | |
{ | |
// 描画をするやつを破棄 | |
batch.dispose(); | |
font.dispose(); | |
} | |
} | |
class GameOverScene | |
implements Scene | |
{ | |
private boolean initialized = false; | |
private SpriteBatch batch; | |
private BitmapFont font; | |
@Override | |
public void create() | |
{ | |
batch = new SpriteBatch(); | |
font = new BitmapFont(); | |
initialized = true; | |
} | |
@Override | |
public boolean isInitialized() | |
{ | |
return initialized; | |
} | |
@Override | |
public boolean render() | |
{ | |
Gdx.gl.glClearColor(128f / 256, 208f / 256, 48f / 256, 1); | |
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); | |
batch.begin(); | |
font.draw( | |
batch, | |
"Game Over\n-- Please press space key --", | |
Gdx.graphics.getWidth() / 2 - 50, | |
Gdx.graphics.getHeight() / 2); | |
batch.end(); | |
return Gdx.input.isKeyJustPressed(Input.Keys.SPACE); | |
} | |
@Override | |
public void dispose() | |
{ | |
batch.dispose(); | |
font.dispose(); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment