效果
核心类(主要是使用char[][]存储显示信息,StringBuilder用于拼接信息在JTextArea上显示)
public class MyTextPanel extends JTextArea implements MyListener,Runnable, Input {
//宽 88 高 29
private StringBuilder builder;
private char[][] map;
private int updateTime;
private WallContainer walls;
private boolean[] lastKey;
private boolean[] currentKey;
private List<Integer> dirtyKey;
private Player player;
public MyTextPanel(){
initData();
setOther();
startLoop();
}
private void startLoop() {
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.start();
}
private void setOther() {
setFont(new Font("consolas",Font.PLAIN,16));
setEditable(false);
addKeyListener(this);
}
private void initData() {
builder=new StringBuilder();
map=new char[29][88];
updateTime=17;
walls=new WallContainer();
lastKey=new boolean[26];
currentKey=new boolean[26];
dirtyKey=new ArrayList<>();
player=new Player(walls,this);
}
@Override//65
public void keyPressed(KeyEvent e) {
int index=getIndex(e.getKeyCode());
if(index!=-1)setValue(index,true);
}
private void setValue(int index, boolean value) {
currentKey[index]=value;
dirtyKey.add(index);
}
private int getIndex(int keyCode) {
if(keyCode<65||keyCode>90)return -1;
return keyCode-65;
}
@Override
public void keyReleased(KeyEvent e) {
int index=getIndex(e.getKeyCode());
if(index!=-1)setValue(index,false);
}
@Override
public void run() {
while (true){
long start = System.currentTimeMillis();
action();
draw();
long end = System.currentTimeMillis();
if(end-start<updateTime){
sleep(updateTime+start-end);
}
}
}
private void sleep(long time) {
// System.out.println("OK");//测试性能
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void draw() {
clearMap();
drawOther();
postMap();
}
private void postMap() {
builder.setLength(0);
for (char[] aMap : map) {
builder.append(aMap).append("\n");
}
setText(builder.toString());
}
private void drawOther() {
walls.draw(map);
player.draw(map);
}
private void clearMap() {
for (char[] aMap : map) {
Arrays.fill(aMap, ' ');
}
}
private void action() {
player.action();
updateKey();
}
private void updateKey() {
if(dirtyKey.isEmpty())return;
dirtyKey.forEach(index->lastKey[index]=currentKey[index]);
dirtyKey.clear();
}
// 存储按键状态 把监听按键状态 变为查询按键状态
@Override
public boolean keyDown(int keyCode) {
int index=getIndex(keyCode);
if(index==-1)return false;
return !lastKey[index]&¤tKey[index];
}
@Override
public boolean keyUp(int keyCode) {
int index=getIndex(keyCode);
if(index==-1)return false;
return lastKey[index]&&!currentKey[index];
}
@Override
public boolean keyPress(int keyCode) {
int index=getIndex(keyCode);
if(index==-1)return false;
return lastKey[index]&¤tKey[index];
}
}
适配器接口
public interface MyListener extends KeyListener {
@Override
default void keyTyped(KeyEvent e) {
}
}
判断输入接口(用于把按键事件的监听改为查询,更适合游戏)
public interface Input {
boolean keyDown(int keyCode);
boolean keyUp(int keyCode);
boolean keyPress(int keyCode);
}
玩家类
public class Player {
private WallContainer walls;
private Input input;
private int x;
private float y;
private float ySpeed;
private float g;
private int width;
private int height;
private int offsetX;
private int offsetY;
private char dir;
private boolean air;
public Player(WallContainer walls, Input input) {
this.walls = walls;
this.input = input;
x=44;
y=4;
width=6;
height=3;
offsetX=-3;
offsetY=-3;
dir='P';
ySpeed=0;
g=0.1f;
air=false;
}
public void draw(char[][] map){
for (int i = (int) (y+offsetY); i < y+offsetY+height; i++) {
if(i>=0){
Arrays.fill(map[i],x+offsetX,x+offsetX+width,dir);
}
}
}
public void action(){
if(input.keyPress(KeyEvent.VK_D)){
dir='P';
moveH(1);
}else if(input.keyPress(KeyEvent.VK_A)){
dir='q';
moveH(-1);
}
if(air){
if(ySpeed<2){
ySpeed+=g;
}
if(ySpeed>0&&walls.collisionH(x-2,x+2, (int) (y+ySpeed))){//下降 且要碰撞对象
while (!walls.collisionH(x-2,x+2, (int) (y+1))){
y+=1;
}
ySpeed=0;
air=false;
}else {//上升不管
y+=ySpeed;
}
}else {
if(!walls.collisionH(x-2,x+2, (int) (y+1))){
air=true;
}
if(input.keyDown(KeyEvent.VK_J)){
ySpeed=-2;
y-=1;
}
}
}
private void moveH(int speed) {
if(!walls.collisionV(x+speed*3,(int)(y-height), (int) y)){
x+=speed;
}
}
}
墙类
public class Wall {
private int x;
private int y;
private int width;
private int height;
public Wall(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void draw(char[][] map){
for (int i = y; i < y+height; i++) {
Arrays.fill(map[i],x,x+width,'W');
}
}
public boolean collisionH(int x1,int x2,int y){
if(x2<x||x1>x+width)return false;
if(y<this.y||y>this.y+height)return false;
return true;
}
public boolean collisionV(int x,int y1,int y2){
if(y2<y||y1>y+height)return false;
if(x<this.x||x>this.x+width)return false;
return true;
}
}
墙类容器类(用于判断与墙的碰撞,以及绘制墙)
public class WallContainer {
private List<Wall> hWall;
private List<Wall> vWall;
public WallContainer() {
hWall=new ArrayList<>();
vWall=new ArrayList<>();
initData();
}
private void initData() {
hWall.add(new Wall(0,26,88,3));
hWall.add(new Wall(22,13,44,3));
vWall.add(new Wall(0,0,4,26));
vWall.add(new Wall(84,0,4,26));
}
public boolean collisionH(int x1,int x2,int y){
for (Wall wall:hWall){
if(wall.collisionH(x1, x2, y))return true;
}
return false;
}
public boolean collisionV(int x,int y1,int y2){
for (Wall wall:vWall){
if(wall.collisionV(x, y1, y2))return true;
}
return false;
}
public void draw(char[][] map){
hWall.forEach(wall -> wall.draw(map));
vWall.forEach(wall -> wall.draw(map));
}
}
主启动类(实际循环等逻辑应该放到该类中,MyTextPanel应该只负责显示)
public class MainGame extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(MainGame::new);
}
public MainGame() throws HeadlessException{
setSize(802,604);
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(new MyTextPanel());
setResizable(false);
setVisible(true);
}
}
实际,只是转换一下输出形式,其他游戏逻辑都可以按之前的写