一、游戏开发思路:
1.Frame--->BackGround--->Obstacle---->BufferedImage,人物等
2.BackGround的构造函数:
只要记住窗口里边的每一个场景,只要游戏一开始就已经出现在屏幕里边的,都是在构造函数里边
3.绘制上地面:
上地面要铺满整个第一关背景图片,由于BackGround的宽度为800,一个上地面的图片的宽度为30,所以要进行27次循环才能把BackGround的宽度铺满
4.Obstacle类:
要有坐标,以及当前是什么障碍物类型,图片的显示以及当前的场景对象(用于关联每一关的不同的障碍物对象)
//用于表示当前的坐标
private int x;
private int y;
//用于记录障碍物类型
private int type; //type1 是上地面 type2是下地面
//用于显示图像 !!!!
private BufferedImage show =null;
//定义当前的场景对象 !!!
private BackGround bg =null;
5.把Obstacle类加载到BackGround里边,由于每一关都有不一样的障碍物,所以BackGround是和Obstacle相关联的,在BackGround类里边设置一个存放障碍物的集合,把我们原先已经定义好的Obstacle对象放到这个集合中去
6.马里奥类
用来获取障碍物的信息那个很重要!!!!
//用于表示横纵坐标
private int x;
private int y;
//用于表示当前的状态
private String status;
//用于显示当前状态对应的图像
private BufferedImage show=null;
//定义一个BackGround对象,用来获取障碍物的信息!!!
private BackGround background=new BackGround();
//用于实现马里奥的动作
private Thread thread=null;
二、游戏场景设计示例图:
第一个场景:
分析图片:
我们以结果为导向,以项目要求为导向,分析出整个项目的要求来进行游戏制作
以左上角为(0,0)起点
1.上地面
1)位置
距离天空最上面的是420cm,只有一排上地面
2.下地面
1)位置
使用双重for循环来控制位置,外层循环控制第几列,内层循环控制生成一排的地面
一共有五层下地面---泥土,每一排循环完之后,还要再循环生成四排
3.水管
1)位置
最上边的3和4是特殊的两个水管图片,所以在for循环里边应该用if条件判断来单独画这两个的水管图片的位置----->怎么单独控制?找他俩的共同点:纵坐标都是360
4.砖块
1)位置
说明:这里的砖块的纵坐标有一点问题,应该是
G的纵坐标是 240
A这一排的纵坐标是300
2)是否可被破坏
其中:
B、D、F是可被破坏的 ------> 对应type是0
不可被破坏的砖块 ------> 对应type是7
第二个场景:
第三个场景:
其他两关的设置是类似的,这里先不再赘述了,以后有时间再讲后两关的设计
三、马里奥人物设计:
1.创建马里奥的类对象:
1.关于马里奥自身的一些属性:
1) 位置坐标
马里奥的X,Y坐标
2)状态
表示马里奥当前是站立还是跳跃还是死亡
3)状态图片
用于以后在MyFrame里边画出来,画的图像就是马里奥类里边定义的这个图象
2.涉及到马里奥日后行为可能会涉及到的:
4)一个BackGround对象,用于获取障碍物的信息
由于马里奥以后会撞碎可以撞碎的砖块,也可以踩死怪物,或者被怪物杀死,或者被食人花杀死,所以要定义一个障碍物的对象
5)一个线程用于实现马里奥的动作
以后在这个游戏中会有同时怪物在动,马里奥在移动,食人花在不停的进出,这些都属于是线程,也就是说一个游戏里边同时有这几个移动的东西,这些都需要用程序来控制他们
2.把马里奥画在窗口里边
只需要在窗口里边用graphics调用drawImage方法即可
但是由于我们把mario里边的坐标等变量属性设置成了private,所以由于访问修饰符的限制,private修饰的变量,只允许在这个类里边访问,那么我们怎么获取它的属性值呢?
可以在类里边实现x,y, show的getter和setter方法
而为什么要把变量设置为private,这就涉及到我们上次讲的javabean封装,确保数据的安全性,不会随便被别人修改
四、支撑知识点:
1.自加运算符
比如a+=1
就相当于a=a+1
2.增强for循环
1)用途:用来遍历集合、数组
2)分类:
遍历集合
for(集合元素的类型 局部变量 : 集合对象)
内部代码仍然调用迭代器
for(Object obj : coll){
System.out.println(obj);
}
遍历数组
for(数组元素的类型 局部变量 : 数组对象)
for (int i : arr){
System.out.println(i);
}
3.多线程(重点和难点)
但这里只简单学一下,毕竟将给新生听的,等到时候我出一个面经背诵和学习专栏就可能要研究深入一点了!!
深入学习多线程可参考:多线程(看这一篇就够了,超详细,满满的干货)_线程csdn-CSDN博客
简单来讲就是一个程序同时做多件事情
五、代码展示:
1.目前的MyFrame类代码:
package com.View;
import com.Constants.StaticValue;
import com.game.Mario;
import com.game.Obstacle;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
public class MyFrame extends JFrame implements KeyListener {
//为什么要存储场景 TODO惹??
//BackGround类的list,里边全部是background类,不是buffered图片,要区别开来
//用于存储 所有的背景
private List<BackGround> allBg =new ArrayList<>();
//用于存储 当前的背景
private BackGround nowBg =new BackGround();
//用于双缓存
//定义一个变量用于双缓存 TODO????????
private Image offScreenImage =null;
//马里奥对象
private Mario mario = new Mario();
//无参构造
public MyFrame() {
//设置窗口的大小为800*600
this.setSize(800,600);
//设置窗口的居中显示
this.setLocationRelativeTo(null);
//设置窗口的可见性
this.setVisible(true);
//设置点击窗口上的关闭键,结束程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口大小不可变
this.setResizable(false);
//向窗口对象添加键盘监听器
this.addKeyListener(this);
//设置窗口名称
this.setTitle("超级玛丽");
//初始化图片
StaticValue.init();
//初始化马里奥
mario =new Mario(10,395);
//创建全部的场景
for(int i=1;i<=3;i++){
allBg.add(new BackGround(i, i==3? true:false));
}
//将第一个场景设置为当前的场景
nowBg =allBg.get(0);
//绘制图像 进行图像的绘制
repaint();
}
@Override
public void paint(Graphics g) {
if(offScreenImage ==null){
offScreenImage =createImage(800,600);
}
Graphics graphics = offScreenImage.getGraphics();
graphics.fillRect(0,0,800,600);
//fillRect 用于在指定的矩形内绘制一个填充的矩形
//绘制背景 将图像绘制到了缓冲区上
graphics.drawImage(nowBg.getBgImage(), 0,0,this);
//绘制障碍物
for(Obstacle ob : nowBg.getObstacleList()){
graphics.drawImage(ob.getShow(),ob.getX(),ob.getY(),this);
}
//绘制马里奥
graphics.drawImage(mario.getShow(),mario.getX(),mario.getY(),this); //注意马里奥绘制要在g.drawImage之前
//将缓冲区的图片绘制到窗口中
g.drawImage(offScreenImage,0,0,this);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
public static void main(String[] args) {
new MyFrame();
}
}
2.BackGround类
package com.View;
import com.Constants.StaticValue;
import com.game.Obstacle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
//加载图片常量之后方便日后的使用
public class BackGround {
//当前场景要显示的图片
private BufferedImage bgImage =null;
//记录当前是第几个场景 玩到了第几关
private int sort;
//判断是否是最后一个场景
private boolean flag;
//空参构造
//用于存放我们所有的障碍物
private List<Obstacle> obstacleList =new ArrayList<>();
public BackGround() {
}
public BufferedImage getBgImage() {
return bgImage;
}
public int getSort() {
return sort;
}
public boolean isFlag() {
return flag;
}
public BackGround(int sort, boolean flag) {
this.sort = sort;
this.flag = flag;
if(flag) { //表示此时为最后一个场景
bgImage = StaticValue.bg2;
}else{
bgImage = StaticValue.bg;
}
//只要记住窗口里边的每一个场景,只要游戏一开始就已经出现在屏幕里边的,都是在构造函数里边
//判断是否是第一关
if(sort==1){
//绘制第一关的地面,上地面type=1,下地面type=2
for (int i = 0; i <= 26; i++) {
obstacleList.add(new Obstacle(420,i*30,1,this));
}
for (int i = 1; i <=5; i++) {
for(int j = 0; j <= 26; j++){
obstacleList.add(new Obstacle(420+i*30,j*30,2,this));
}
}
//绘制砖块A
for (int i = 120; i <=150; i+=30) {
obstacleList.add(new Obstacle(300,i,7,this));
}
//绘制砖块B-F
for (int i = 300; i <=570; i+=30) { //0是普通砖块 7是不能被破坏的砖块
if(i==360||i==390||i==480||i==510||i==540){
obstacleList.add(new Obstacle(300,i,7,this));
}else{
obstacleList.add(new Obstacle(300,i,0,this));
}
}
//绘制砖块G
for (int i = 420; i <=450 ; i+=30) {
obstacleList.add(new Obstacle(240,i,7,this));
}
//绘制水管
for (int i = 360; i <=600 ; i+=25) {
if(i==360){ //纵坐标为360.最高的两个水管图片
obstacleList.add(new Obstacle(i,620,3,this));
obstacleList.add(new Obstacle(i,645,4,this));
}else{
obstacleList.add(new Obstacle(i,620,5,this));
obstacleList.add(new Obstacle(i,645,6,this));
}
}
}
}
public List<Obstacle> getObstacleList() {
return obstacleList;
}
}
3.马里奥类
package com.game;
import com.Constants.StaticValue;
import com.View.BackGround;
import java.awt.image.BufferedImage;
public class Mario implements Runnable{
//用于表示横纵坐标
private int x;
private int y;
//用于表示当前的状态
private String status;
//用于显示当前状态对应的图像
private BufferedImage show=null;
//定义一个BackGround对象,用来获取障碍物的信息!!!
private BackGround background=new BackGround();
//用于实现马里奥的动作
private Thread thread=null;
//马里奥的移动速度
//马里奥的跳跃速度
public Mario() {
}
public Mario(int x, int y) {
this.x = x;
this.y = y;
show = StaticValue.stand_R; //初始动作是向右站立
this.status="stand-right";
thread=new Thread(this); //创建线程
thread.start();//启动线程
}
//马里奥向左移动
//马里奥向右移动
//马里奥向左停止
//马里奥向右停止
@Override
public void run() {
}
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;
}
public BufferedImage getShow() {
return show;
}
public void setShow(BufferedImage show) {
this.show = show;
}
}
4.障碍物类
package com.game;
import com.Constants.StaticValue;
import com.View.BackGround;
import java.awt.image.BufferedImage;
public class Obstacle {
//用于表示当前的坐标
private int x;
private int y;
//用于记录障碍物类型
private int type; //type1 是上地面 type2是下地面
//用于显示图像 !!!!
private BufferedImage show =null;
//定义当前的场景对象 !!!
private BackGround bg =null;
public Obstacle(int y, int x, int type, BackGround bg) {
this.y = y;
this.x = x;
this.type = type;
this.bg = bg;
show = StaticValue.obstacle.get(type); //得到当前的障碍物类型,在StaticValue第三十行左右
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getType() {
return type;
}
public BufferedImage getShow() {
return show;
}
}