Created
February 26, 2016 07:40
-
-
Save argius/2472a632bc55d0215af7 to your computer and use it in GitHub Desktop.
ゲームパッド入力とキーボード入力を使って宇宙船からビームを発射する。(Java JInputサンプル)
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
/* | |
* ゲームパッド入力とキーボード入力を使って宇宙船からビームを発射する。 | |
*/ | |
import java.awt.*; | |
import java.awt.image.BufferedImage; | |
import java.io.*; | |
import java.util.*; | |
import java.util.List; | |
import java.util.concurrent.ForkJoinPool; | |
import java.util.stream.*; | |
import javax.imageio.ImageIO; | |
import javax.swing.*; | |
import net.java.games.input.*; | |
import net.java.games.input.Component.Identifier; | |
import net.java.games.input.Component; | |
import net.java.games.input.EventQueue; | |
/** | |
* メインプログラム。 | |
*/ | |
public final class App { | |
static final int SCREEN_WIDTH = 400; | |
static final int SCREEN_HEIGHT = 500; | |
GameScreen screen; | |
ControllerInput keyboardInput; | |
ControllerInput gamepadInput; | |
Starship starship; | |
App() { | |
this.starship = new Starship(); | |
this.gamepadInput = new GamepadControllerInput(); | |
this.keyboardInput = new KeyboardControllerInput(); | |
this.screen = new GameScreen(starship); | |
} | |
void startGame() { | |
// ゲームパッド入力 | |
if (gamepadInput.available()) { | |
ForkJoinPool.commonPool().execute(() -> { | |
while (true) { | |
gamepadInput.getState().ifPresent(this::changeState); | |
sleep(15L); | |
} | |
}); | |
} | |
// キーボード入力 | |
if (keyboardInput.available()) { | |
ForkJoinPool.commonPool().execute(() -> { | |
while (true) { | |
keyboardInput.getState().ifPresent(this::changeState); | |
sleep(15L); | |
} | |
}); | |
} | |
// 状態更新 | |
ForkJoinPool.commonPool().execute(() -> { | |
while (true) { | |
starship.updateState(); | |
screen.repaint(); | |
sleep(15L); | |
} | |
}); | |
} | |
/** | |
* コントローラー入力の状態をゲームの状態に反映する。 | |
* @param controllerState コントローラ入力の状態 | |
*/ | |
void changeState(ControllerInput.State controllerState) { | |
starship.directionX = controllerState.x; | |
starship.directionY = controllerState.y; | |
if (controllerState.attackButtonPushed) { | |
starship.fire(); | |
} | |
} | |
static void sleep(long millis) { | |
try { | |
Thread.sleep(millis); | |
} catch (InterruptedException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public static void main(String[] args) { | |
SwingUtilities.invokeLater(new Runnable() { | |
public void run() { | |
JFrame f = new JFrame("Game"); | |
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); | |
f.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); | |
f.setLocationRelativeTo(null); | |
App app = new App(); | |
f.add(app.screen, BorderLayout.CENTER); | |
f.setVisible(true); | |
app.startGame(); | |
} | |
}); | |
} | |
} | |
/** | |
* ゲーム画面。 | |
*/ | |
final class GameScreen extends JPanel { | |
Starship starship; | |
GameScreen(Starship starship) { | |
this.starship = starship; | |
setBackground(Color.BLACK); | |
} | |
@Override | |
protected void paintComponent(Graphics g) { | |
super.paintComponent(g); | |
g.drawImage(starship.image, starship.x, starship.y, this); | |
g.setColor(Color.CYAN); | |
for (Beam beam : starship.beams) { | |
if (beam.fired) { | |
g.fill3DRect(beam.x - 10, beam.y, 4, 8, true); | |
g.fill3DRect(beam.x + 10, beam.y, 4, 8, true); | |
} | |
} | |
} | |
} | |
/** | |
* 宇宙船。 | |
*/ | |
final class Starship { | |
static final int WIDTH = 48; | |
static final int HEIGHT = 48; | |
static final int MOVE_DISTANCE = 12; | |
static final int MAX_X = App.SCREEN_WIDTH - (WIDTH - 8) * 2; | |
static final int MIN_X = 10; | |
static final int MAX_Y = App.SCREEN_HEIGHT - HEIGHT * 2; | |
static final int MIN_Y = 10; | |
BufferedImage image; | |
int directionX; | |
int directionY; | |
int x; | |
int y; | |
List<Beam> beams; | |
Starship() { | |
try { | |
this.image = ImageIO.read(getClass().getResource("starship.png")); | |
} catch (IOException e) { | |
throw new UncheckedIOException(e); | |
} | |
this.directionX = 0; | |
this.directionY = 0; | |
this.x = MAX_X / 2 - 10; | |
this.y = MAX_Y - 30; | |
// ビーム同時発射は最大5発 | |
this.beams = IntStream.range(0, 5).mapToObj(x -> new Beam()).collect(Collectors.toList()); | |
} | |
void move(int directionX, int directionY) { | |
this.directionX = directionX; | |
this.directionY = directionY; | |
} | |
void fire() { | |
Collections.rotate(beams, 1); | |
Beam beam = beams.get(0); | |
if (!beam.fired) { | |
beam.fire(x, y); | |
} | |
} | |
void updateState() { | |
int x = this.x; | |
int y = this.y; | |
if (directionX > 0) { | |
x += MOVE_DISTANCE; | |
x = Math.min(x, MAX_X); | |
} | |
else if (directionX < 0) { | |
x -= MOVE_DISTANCE; | |
x = Math.max(x, MIN_X); | |
} | |
if (directionY > 0) { | |
y += MOVE_DISTANCE; | |
y = Math.min(y, MAX_Y); | |
} | |
else if (directionY < 0) { | |
y -= MOVE_DISTANCE; | |
y = Math.max(y, MIN_Y); | |
} | |
this.x = x; | |
this.y = y; | |
for (Beam beam : beams) { | |
beam.updateState(); | |
} | |
this.directionX = 0; | |
this.directionY = 0; | |
} | |
} | |
/** | |
* ビーム。 | |
*/ | |
final class Beam { | |
static final int MOVE_DISTANCE = 18; | |
boolean fired; | |
int x; | |
int y; | |
void fire(int x, int y) { | |
this.fired = true; | |
this.x = x + Starship.WIDTH / 2; | |
this.y = y; | |
} | |
void fadeOut() { | |
this.fired = false; | |
} | |
void updateState() { | |
if (fired) { | |
y -= MOVE_DISTANCE; | |
if (y < -20) { | |
fadeOut(); | |
} | |
} | |
} | |
} | |
/** | |
* コントローラー入力。 | |
*/ | |
interface ControllerInput { | |
Optional<ControllerInput.State> getState(); | |
boolean available(); | |
static Controller detectController(Controller.Type type) { | |
Controller[] controllers = ControllerEnvironment.getDefaultEnvironment().getControllers(); | |
for (Controller controller : controllers) { | |
if (controller != null && controller.getType() == type) { | |
return controller; | |
} | |
} | |
return NullController.INSTANCE; | |
} | |
/** | |
* コントローラー入力の状態。 | |
*/ | |
static final class State { | |
int x; | |
int y; | |
boolean attackButtonPushed; | |
State(int x, int y, boolean attackButtonPushed) { | |
this.x = x; | |
this.y = y; | |
this.attackButtonPushed = attackButtonPushed; | |
} | |
} | |
} | |
/** | |
* コントローラーが無効な場合のスタブ。 | |
*/ | |
final class NullController implements Controller { | |
static final Controller INSTANCE = new NullController(); | |
@Override | |
public Controller[] getControllers() { | |
return new Controller[0]; | |
} | |
@Override | |
public Type getType() { | |
return Controller.Type.UNKNOWN; | |
} | |
@Override | |
public Component[] getComponents() { | |
return new Component[0]; | |
} | |
@Override | |
public Component getComponent(Identifier id) { | |
return null; | |
} | |
@Override | |
public Rumbler[] getRumblers() { | |
return new Rumbler[0]; | |
} | |
@Override | |
public boolean poll() { | |
return false; | |
} | |
@Override | |
public void setEventQueueSize(int size) { | |
} | |
@Override | |
public EventQueue getEventQueue() { | |
return null; | |
} | |
@Override | |
public PortType getPortType() { | |
return Controller.PortType.UNKNOWN; | |
} | |
@Override | |
public int getPortNumber() { | |
return -1; | |
} | |
@Override | |
public String getName() { | |
return "NullController"; | |
} | |
} | |
/** | |
* ゲームパッドコントローラー入力。 | |
*/ | |
final class GamepadControllerInput implements ControllerInput { | |
final Controller controller; | |
volatile boolean button3Released; | |
GamepadControllerInput() { | |
this.controller = ControllerInput.detectController(Controller.Type.GAMEPAD); | |
this.button3Released = true; | |
} | |
@Override | |
public boolean available() { | |
return controller != NullController.INSTANCE; | |
} | |
@Override | |
public Optional<ControllerInput.State> getState() { | |
if (controller.poll()) { | |
float x0 = controller.getComponent(Identifier.Axis.X).getPollData(); | |
float y0 = controller.getComponent(Identifier.Axis.Y).getPollData(); | |
int x = x0 > 0.0f | |
? 1 | |
: x0 < -0.1f | |
? -1 | |
: 0; | |
int y = y0 > 0.0f | |
? 1 | |
: y0 < -0.1f | |
? -1 | |
: 0; | |
// Button._2はボタン3(0オリジン) | |
boolean button3Pushed = controller.getComponent(Identifier.Button._2).getPollData() > 0.0f; | |
boolean attackButtonPushed = button3Released && button3Pushed; | |
button3Released = !button3Pushed; | |
if (x != 0 || y != 0 || attackButtonPushed) { | |
return Optional.of(new ControllerInput.State(x, y, attackButtonPushed)); | |
} | |
} | |
return Optional.empty(); | |
} | |
} | |
/** | |
* キーボードコントローラー入力。 | |
*/ | |
final class KeyboardControllerInput implements ControllerInput { | |
final Controller controller; | |
volatile boolean spaceKeyReleased; | |
KeyboardControllerInput() { | |
this.controller = ControllerInput.detectController(Controller.Type.KEYBOARD); | |
this.spaceKeyReleased = true; | |
} | |
@Override | |
public boolean available() { | |
return controller != NullController.INSTANCE; | |
} | |
@Override | |
public Optional<ControllerInput.State> getState() { | |
if (controller.poll()) { | |
int x = controller.getComponent(Identifier.Key.LEFT).getPollData() > 0.0f | |
? -1 | |
: controller.getComponent(Identifier.Key.RIGHT).getPollData() > 0.0f | |
? 1 | |
: 0; | |
int y = controller.getComponent(Identifier.Key.UP).getPollData() > 0.0f | |
? -1 | |
: controller.getComponent(Identifier.Key.DOWN).getPollData() > 0.0f | |
? 1 | |
: 0; | |
// Spaceキー | |
boolean spaceKeyPushed = controller.getComponent(Identifier.Key.SPACE).getPollData() > 0.0f; | |
boolean attackButtonPushed = spaceKeyReleased && spaceKeyPushed; | |
spaceKeyReleased = !spaceKeyPushed; | |
if (x != 0 || y != 0 || attackButtonPushed) { | |
return Optional.of(new ControllerInput.State(x, y, attackButtonPushed)); | |
} | |
} | |
return Optional.empty(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment