Server
import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.util.ArrayList;import java.util.List;/*** 执行类*** *///窗口包public class MyFrameC extends JFrame implements KeyListener, Runnable {//给游戏背景添加上图片//用于存储所有的背景、private List<Background> allBg = new ArrayList<>();//用于存储当前背景private Background nowBg = new Background();//用于双缓存//把要显示的东西全部绘制在一张'图片'上(内存开辟),然后再把这张'图片'一次性显示到屏幕上~private Image offScreenImage = null;//马力对象public static Mario mario = new Mario();public static Mario mario2 = new Mario();//定义一个全局变量x,ypublic static int x, y;//接收数据DatagramSocket ds;//发送数据DatagramPacket dp;//坐标类public static MarioXY marioXY = new MarioXY(50,330);private int a;private int state;//定义一个线程对象,用于实现马力的运动private Thread thread = new Thread(this);static String str;static Integer i;//空参构造方法//在创建对象的时候就把窗口创建出来public MyFrameC() {//给MarioXY赋值marioXY = new MarioXY(20, 330);//设置窗口大小为800 * 600this.setSize(1000, 600);//设置窗口居中显示this.setLocationRelativeTo(null);//设置窗口的可见性this.setVisible(true);//设置点击窗口上的关闭键,结束程序this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口大小不可变this.setResizable(false);//向窗口对象添加键盘监听器 这里我们需要继承一个接口KeyListenerthis.addKeyListener(this);//设置窗口名称this.setTitle("超级马力");//调用静态初始化方法 初始化图片StaticValue.init();//初始化马力对象mario = new Mario(marioXY.getX(), marioXY.getY(), marioXY);//创建全部的场景 使用for循环for (int i = 1; i <= 3; i++) {//判断是否是第三关 传入i值allBg.add(new Background(i, i == 3 ? true : false));}//设置第一个场景nowBg = allBg.get(0);//将第一个场景传给马力对象mario.setBackground(nowBg);//接收对象/* new Thread(() -> {try {ds = new DatagramSocket(8888);byte[] bytes = new byte[1024];while (true) {dp = new DatagramPacket(bytes, bytes.length);ds.receive(dp);//反序列化ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream oi = new ObjectInputStream(bais);marioXY = (MarioXY) oi.readObject();}} catch (Exception e) {e.printStackTrace();}}).start();*///发送对象thread.start();repaint();}//重写paint方法public void paint(Graphics g) {//首先判断这个双缓冲是否为nullif (offScreenImage == null) {//如果是的话我们为他赋值offScreenImage = createImage(1000, 600);}Graphics graphics = offScreenImage.getGraphics();//绘制graphics.fillRect(0, 0, 1000, 600);//对图像进行填充//判断游戏模式graphics.setColor(Color.BLUE);graphics.drawString("开始游戏", 500, 300);graphics.setFont(new Font("仿体", Font.BOLD, 100));if (state == 1) {//绘制背景 执行到这的时候nowBg里面已经存入了图片了graphics.drawImage(nowBg.getBgImage(), 0, 0, this);//绘制的障碍物for (Obstacle ob : nowBg.getObstacleList()) {graphics.drawImage(ob.getShow(), ob.getX(), ob.getY(), this);}//绘制城堡graphics.drawImage(nowBg.getTower(), 620, 270, this);//绘制旗杆graphics.drawImage(nowBg.getGan(), 500, 220, this);//绘制马力graphics.drawImage(mario.getShow(), mario.getX(), mario.getY(), this);//上面的步骤是将图片已经绘制到缓冲区上了//绘制敌人for (Enemy enemy : nowBg.getEnemyList()) {graphics.drawImage(enemy.getShow(), enemy.getX(), enemy.getY(), this);}}g.drawImage(offScreenImage, 0, 0, this);}// 下面我们将图片绘制到窗口中@Overridepublic void keyTyped(KeyEvent e) {}//当键盘按下按键时调用//这个方法是 当键盘按下某个按键时调用@Overridepublic void keyPressed(KeyEvent e) {//向右移动if (e.getKeyCode() == 68) {mario.rightMove();try {ds = new DatagramSocket();//序列化对象 把对象序列化ByteArrayOutputStream bao = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bao);oos.writeObject(marioXY);oos.flush();byte[] sendBuff = bao.toByteArray();dp = new DatagramPacket(sendBuff, sendBuff.length,InetAddress.getLocalHost(), 8787);ds.send(dp);} catch (Exception e1) {e1.printStackTrace();}}//向左移动if (e.getKeyCode() == 65) {mario.leftMove();}//跳跃if (e.getKeyCode() == 87) {mario.jump();}int key = e.getKeyCode();switch (key) {case KeyEvent.VK_ENTER:state = 1;break;}}//当键盘按下按键时调用//这个方法是 当键盘松开某个按键时调用@Overridepublic void keyReleased(KeyEvent e) {//向左停止if (e.getKeyCode() == 65) {mario.leftStop();}//向右停止if (e.getKeyCode() == 68) {mario.rightStop();}}@Overridepublic void run() {while (true) {//重新绘制图像repaint();try {//线程休眠Thread.sleep(50);//判断马力是否已经走到了最右边if (mario.getX() >= 980) {nowBg = allBg.get(nowBg.getSort());mario.setBackground(nowBg);mario.setX(10);mario.setY(370);}if (mario.getX() <= 0) {nowBg = allBg.get(nowBg.getSort() - 2);mario.setBackground(nowBg);mario.setX(980);mario.setY(370);}} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {MyFrameC m = new MyFrameC();}}
Client
import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.util.ArrayList;import java.util.List;/*** 执行类*** *///窗口包public class MyFrameC extends JFrame implements KeyListener, Runnable {//给游戏背景添加上图片//用于存储所有的背景、private List<Background> allBg = new ArrayList<>();//用于存储当前背景private Background nowBg = new Background();//用于双缓存//把要显示的东西全部绘制在一张'图片'上(内存开辟),然后再把这张'图片'一次性显示到屏幕上~private Image offScreenImage = null;//马力对象public static Mario mario = new Mario();public static Mario mario2 = new Mario();//定义一个全局变量x,ypublic static int x, y;//接收数据DatagramSocket ds;//发送数据DatagramPacket dp;//坐标类public static MarioXY marioXY = new MarioXY(50,330);private int a;private int state;//定义一个线程对象,用于实现马力的运动private Thread thread = new Thread(this);static String str;static Integer i;//空参构造方法//在创建对象的时候就把窗口创建出来public MyFrameC() {//给MarioXY赋值marioXY = new MarioXY(20, 330);//设置窗口大小为800 * 600this.setSize(1000, 600);//设置窗口居中显示this.setLocationRelativeTo(null);//设置窗口的可见性this.setVisible(true);//设置点击窗口上的关闭键,结束程序this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口大小不可变this.setResizable(false);//向窗口对象添加键盘监听器 这里我们需要继承一个接口KeyListenerthis.addKeyListener(this);//设置窗口名称this.setTitle("超级马力");//调用静态初始化方法 初始化图片StaticValue.init();//初始化马力对象mario = new Mario(marioXY.getX(), marioXY.getY(), marioXY);//创建全部的场景 使用for循环for (int i = 1; i <= 3; i++) {//判断是否是第三关 传入i值allBg.add(new Background(i, i == 3 ? true : false));}//设置第一个场景nowBg = allBg.get(0);//将第一个场景传给马力对象mario.setBackground(nowBg);//接收对象/* new Thread(() -> {try {ds = new DatagramSocket(8888);byte[] bytes = new byte[1024];while (true) {dp = new DatagramPacket(bytes, bytes.length);ds.receive(dp);//反序列化ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream oi = new ObjectInputStream(bais);marioXY = (MarioXY) oi.readObject();}} catch (Exception e) {e.printStackTrace();}}).start();*///发送对象thread.start();repaint();}//重写paint方法public void paint(Graphics g) {//首先判断这个双缓冲是否为nullif (offScreenImage == null) {//如果是的话我们为他赋值offScreenImage = createImage(1000, 600);}Graphics graphics = offScreenImage.getGraphics();//绘制graphics.fillRect(0, 0, 1000, 600);//对图像进行填充//判断游戏模式graphics.setColor(Color.BLUE);graphics.drawString("开始游戏", 500, 300);graphics.setFont(new Font("仿体", Font.BOLD, 100));if (state == 1) {//绘制背景 执行到这的时候nowBg里面已经存入了图片了graphics.drawImage(nowBg.getBgImage(), 0, 0, this);//绘制的障碍物for (Obstacle ob : nowBg.getObstacleList()) {graphics.drawImage(ob.getShow(), ob.getX(), ob.getY(), this);}//绘制城堡graphics.drawImage(nowBg.getTower(), 620, 270, this);//绘制旗杆graphics.drawImage(nowBg.getGan(), 500, 220, this);//绘制马力graphics.drawImage(mario.getShow(), mario.getX(), mario.getY(), this);//上面的步骤是将图片已经绘制到缓冲区上了//绘制敌人for (Enemy enemy : nowBg.getEnemyList()) {graphics.drawImage(enemy.getShow(), enemy.getX(), enemy.getY(), this);}}g.drawImage(offScreenImage, 0, 0, this);}// 下面我们将图片绘制到窗口中@Overridepublic void keyTyped(KeyEvent e) {}//当键盘按下按键时调用//这个方法是 当键盘按下某个按键时调用@Overridepublic void keyPressed(KeyEvent e) {//向右移动if (e.getKeyCode() == 68) {mario.rightMove();try {ds = new DatagramSocket();//序列化对象 把对象序列化ByteArrayOutputStream bao = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bao);oos.writeObject(marioXY);oos.flush();byte[] sendBuff = bao.toByteArray();dp = new DatagramPacket(sendBuff, sendBuff.length,InetAddress.getLocalHost(), 8787);ds.send(dp);} catch (Exception e1) {e1.printStackTrace();}}//向左移动if (e.getKeyCode() == 65) {mario.leftMove();}//跳跃if (e.getKeyCode() == 87) {mario.jump();}int key = e.getKeyCode();switch (key) {case KeyEvent.VK_ENTER:state = 1;break;}}//当键盘按下按键时调用//这个方法是 当键盘松开某个按键时调用@Overridepublic void keyReleased(KeyEvent e) {//向左停止if (e.getKeyCode() == 65) {mario.leftStop();}//向右停止if (e.getKeyCode() == 68) {mario.rightStop();}}@Overridepublic void run() {while (true) {//重新绘制图像repaint();try {//线程休眠Thread.sleep(50);//判断马力是否已经走到了最右边if (mario.getX() >= 980) {nowBg = allBg.get(nowBg.getSort());mario.setBackground(nowBg);mario.setX(10);mario.setY(370);}if (mario.getX() <= 0) {nowBg = allBg.get(nowBg.getSort() - 2);mario.setBackground(nowBg);mario.setX(980);mario.setY(370);}} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {MyFrameC m = new MyFrameC();}}
坐标系类
import java.io.Serializable;public class MarioXY implements Serializable {private int x;private int y;public MarioXY(){}public MarioXY(int x, int y ){this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}}
Mario类
import java.awt.image.BufferedImage;/*** 实现Runnable接口 线程* 马力类** */public class Mario implements Runnable {//用于表示横纵坐标private int x;private int y;private MarioXY xy;//用于表示当前的状态private String status;//用于显示当前状态对应的图像private BufferedImage show = null;//定义一个Background对象,用来获取障碍物的信息private Background background = new Background();//用来实现马力的动作private Thread thread = null;//马力的移动速度private int xSpeed;//马力的跳跃速度private int ySpeed;//定义一个索引,这个变量用于取得马力的运动图像private int index;//定义一个int变量 表示马力上升的时间 超过一定时间都不能上升了private int upTime = 0;//马力死亡private boolean isDeath = false;//坐标MarioXY marioXY;//用来实现马力奥的动作public Mario() {}public Mario(int x, int y,MarioXY marioXY) {//用于表示马力的位置this.x = marioXY.getX();this.y = marioXY.getY();this.marioXY = marioXY;//用于表示马力的初始站立样子show = StaticValue.stand_R;//用于马力当前状态this.status = "stand--right";//初始化线程thread = new Thread(this);thread.start();}//马力死亡的方法public void death(){isDeath = true;}//马力向左移动public void leftMove() {//改变移动速度xSpeed = -10;//判断马力是否处于空中,由于处于空中无法移动//indexOf传入一个值 返回他出现的第一个索引位置if (status.indexOf("jump") != -1) {status = "jump--left";} else {status = "move--left";}}//马力向右移动public void rightMove() {//速度xSpeed = 10;if (status.indexOf("jump") != -1) {status = "jump--right";} else {status = "move--right";}}//马力向左停止public void leftStop() {xSpeed = 0;if (status.indexOf("jump") != -1) {status = "jump--left";} else {status = "stop--left";}}//马力向右停止public void rightStop() {xSpeed = 0;if (status.indexOf("jump") != -1) {status = "jump--right";} else {status = "stop--right";}}/*垃圾方法//马力向上跳跃public void jump_up() {ySpeed = 10;if (status.indexOf("jump" ) != -1) {status = "stop-right";}else {status = "jump--up";}}//马力向右上跳停止public void jump_stop(){ySpeed = -10;if (status.indexOf("jump") != -1){status = "right--stop";}else { 垃圾方法status = "jump--stop";}}*///马力攻击public void gongji() {}//马力跳跃public void jump() {//判断是否是跳跃状态 如果是-1的话就是 此时马力是跳跃状态if (status.indexOf("jump") == -1) {//判断马力的方向是那边 如果不等于-1的话 此时马力的方向是左if (status.indexOf("left") != -1) {status = "jump--left";} else {status = "jump--right";}ySpeed = -10;//跳跃的像素高度upTime = 10;}}//马力下落public void fall() {if (status.indexOf("left") != -1) {status = "jump--left";} else {status = "jump--right";}ySpeed = 10;}@Overridepublic void run() {while (true) {//判断是否处于障碍物上boolean onObstacle = false;//判断是否可以往右走boolean canRight = true;//判断是否可以往左走boolean canLeft = true;//判断马力是否到达旗杆的位置//遍历当前场景里所有的障碍物for (int i = 0; i < background.getObstacleList().size(); i++) {Obstacle ob = background.getObstacleList().get(i);//判断马力是否站立于障碍物之上if (ob.getY() == this.y + 30 && (ob.getX() > this.x - 30 && ob.getX() < this.x + 30)) {onObstacle = true;}//判断马力是否跳起来顶到方块if (ob.getY() >= this.y - 30 && (ob.getY() <= this.y - 20) && (ob.getX() > this.x - 30) && ob.getX() < this.x + 30) {if (ob.getType() == 0) {//删除可破坏的砖块background.getObstacleList().remove(ob);}//当顶到砖块的时候 upTime就为0就会直接下落了upTime = 0;}//判断马力是否可以往右走 如果符合这个条件说明马力的右边有障碍物//更改了一下if (ob.getX() == this.x + 30 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 30)) {canRight = false;}//判断是否可以往左边走 如果符合条件就说明马力的左边有障碍物if (ob.getX() == this.x - 30 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 30)) {canLeft = false;}}//判断马力是否碰到敌人死亡或踩死蘑菇敌人for (int i = 0; i < background.getEnemyList().size(); i++) {Enemy e = background.getEnemyList().get(i);if (e.getY() == this.y + 20 && (e.getX() - 25 <= this.x && e.getX() + 35 >= this.x)) {if (e.getType() == 1) {e.death();upTime = 3;ySpeed -= 10;}}if (e.getX() + 35 > this.x && e.getX() - 25 < this.x && (e.getY() + 35 > this.y && e.getY() - 20 < this.y)) {//马力死亡death();}}//进行马力跳跃的操作//判断马力是否在障碍物之上并且upTime=0if (onObstacle && upTime == 0) {//判断马力面向的方向是那边 如果是左边就等于-1 否则是右边if (status.indexOf("left") != -1) {//马力是否是移动中if (xSpeed != 0) {//如果xSpeed不等于0就是处于移动状态 向左status = "move--left";} else {//如果xSpeed等于0 就是停止状态 向左status = "stop-left";}} else {//马力是否是移动中if (xSpeed != 0) {//如果xSpeed不等于0就是处于移动状态 向右status = "move--right";} else {//如果xSpeed等于0 就是停止状态 向右status = "stop--right";}}} else {//处于上升状态if (upTime != 0) {//自减 当upTime==0的时候就证明他已经上升到了最高点upTime--;} else {//调用fall()方法让他下落fall();}//这里进行了加 Y轴的y += ySpeed;System.out.println();}if ((canLeft && xSpeed < 0) || (canRight && xSpeed > 0)) {x += xSpeed;//判断马力是否到了屏幕的最左边,就无法再运动了if (x < 0) {x = 0;}}//判断当前是否是移动状态if (status.contains("move")) {//改变图片的索引index = index == 0 ? 1 : 0;}//判断是否向左移动if ("move--left".equals(status)) {show = StaticValue.run_L.get(index);}//判断是否向右移动if ("move--right".equals(status)) {show = StaticValue.run_R.get(index);}//判断是否向左停止if ("stop--left".equals(status)) {show = StaticValue.stand_L;}//判断是否向左停止if ("stop--right".equals(status)) {show = StaticValue.stand_R;}//判断是否向左跳跃if ("jump--left".equals(status)) {show = StaticValue.jump_L;}//判断是否向右跳跃if ("jump--right".equals(status)) {show = StaticValue.jump_R;}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}//实时给坐标类信息marioXY.setX(this.x);marioXY.setY(this.y);}}public int getX() {return x;}public int getY() {return y;}public BufferedImage getShow() {return show;}public void setX(int x) {this.x = x;}public void setY(int Y) {this.y = Y;}public void setShow(BufferedImage show) {this.show = show;}public void setBackground(Background background) {this.background = background;}public MarioXY getXy() {return xy;}public void setXy(MarioXY xy) {this.xy = xy;}}
