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,y
public 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 * 600
this.setSize(1000, 600);
//设置窗口居中显示
this.setLocationRelativeTo(null);
//设置窗口的可见性
this.setVisible(true);
//设置点击窗口上的关闭键,结束程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口大小不可变
this.setResizable(false);
//向窗口对象添加键盘监听器 这里我们需要继承一个接口KeyListener
this.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) {
//首先判断这个双缓冲是否为null
if (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);
}
// 下面我们将图片绘制到窗口中
@Override
public void keyTyped(KeyEvent e) {
}
//当键盘按下按键时调用
//这个方法是 当键盘按下某个按键时调用
@Override
public 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;
}
}
//当键盘按下按键时调用
//这个方法是 当键盘松开某个按键时调用
@Override
public void keyReleased(KeyEvent e) {
//向左停止
if (e.getKeyCode() == 65) {
mario.leftStop();
}
//向右停止
if (e.getKeyCode() == 68) {
mario.rightStop();
}
}
@Override
public 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,y
public 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 * 600
this.setSize(1000, 600);
//设置窗口居中显示
this.setLocationRelativeTo(null);
//设置窗口的可见性
this.setVisible(true);
//设置点击窗口上的关闭键,结束程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口大小不可变
this.setResizable(false);
//向窗口对象添加键盘监听器 这里我们需要继承一个接口KeyListener
this.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) {
//首先判断这个双缓冲是否为null
if (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);
}
// 下面我们将图片绘制到窗口中
@Override
public void keyTyped(KeyEvent e) {
}
//当键盘按下按键时调用
//这个方法是 当键盘按下某个按键时调用
@Override
public 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;
}
}
//当键盘按下按键时调用
//这个方法是 当键盘松开某个按键时调用
@Override
public void keyReleased(KeyEvent e) {
//向左停止
if (e.getKeyCode() == 65) {
mario.leftStop();
}
//向右停止
if (e.getKeyCode() == 68) {
mario.rightStop();
}
}
@Override
public 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;
}
@Override
public 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=0
if (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;
}
}