JAVA游戏入门开发DAY 4 添加主角

这一讲将是正片,可以说得上是创造这个游戏的世界,而且充分利用了JAVA面向对象的特性。


我们在这一讲中,将主角看成一个类,让主角有活动的能力。


为了赋予主角活动的功能,我们将创造一个类,这个类要做的主要是以下三点:

1、管理主角在游戏中的x,y坐标以及移动的速度(将窗体看成是一个坐标系,通过坐标系,我们就可以表达主角的所在位置)

2、随着游戏的进行、用户的输入,更新主角的当前坐标和速度(根据用户输入,更新主角当前移动的位置和速度)

3、允许其他类去使用x,y等参数。


所以,根据以上三点,我这类中的方法大致分为以下3类:

1、一类是更新类,随着游戏的进行,对游戏进行更新。

2、一类是用户输入类,对用户输入进行反馈,如用户按下空格,其实就是让主角跳起来,我们就写一个方法来使得主角跳起来。

3、一类的辅助方法,其他类可以通过这些方法来对主角类里面的参数进行更新。


现在让我们开始吧。


我们在我们在第一讲中的包中,创建一个Robot.java类,然后将一下代码粘贴进去(如果你真正想学东西的话,建议逐个逐个字输入进去,加深记忆和理解)

package kiloboltgame;

import java.awt.Graphics;

public class Robot {
        //In Java, Class Variables should be private so that only its methods can change them.
	private int centerX = 100;
	private int centerY = 382;
	private boolean jumped = false;

	private int speedX = 0;
	private int speedY = 1;

	public void update() {

		// Moves Character or Scrolls Background accordingly.
		if (speedX < 0) {
			centerX += speedX;
		} else if (speedX == 0) {
			System.out.println("Do not scroll the background.");

		} else {
			if (centerX <= 150) {
				centerX += speedX;
			} else {
				System.out.println("Scroll Background Here");
			}
		}
		
		// Updates Y Position

		if (centerY + speedY >= 382) {
			centerY = 382;
		}else{                       
                     centerY += speedY;
                }

		// Handles Jumping
		if (jumped == true) {
			speedY += 1;

			if (centerY + speedY >= 382) {
				centerY = 382;
				speedY = 0;
				jumped = false;
			}

		}

		// Prevents going beyond X coordinate of 0
		if (centerX + speedX <= 60) {
			centerX = 61;
		}
	}

	public void moveRight() {
		speedX = 6;
	}

	public void moveLeft() {
		speedX = -6;
	}

	public void stop() {
		speedX = 0;
	}

	public void jump() {
		if (jumped == false) {
			speedY = -15;
			jumped = true;
		}

	}
}


P.S,这个类其实可以理解为创造了一个游戏的物理世界,如人物移动的速度,落下的速度等。而我们将主角看成是一个类,其实是面向对象程序设计的思想,其实这样思路比初学时候的面向过程设计思想要好,至少代码量少了很多,这里需要大家逻辑上转弯,抽象的去看,将主角看成是一个类,一类物品之一,而在我们这里,这一类物品恰巧只有一个(有点像单类模式设计)。


这些变量都有什么用?

老外在此简单的说明该了一下

1、centerX, centerY代表了主角当前的位置。

2、 speedX, speed Y分别代表主角X、Y方向上的当前速度。

3、 jumped 记录主角有木有跳起来,有就为真,反之亦然。


现在就来让我们来讨论一下各个方法的作用


1.Always called methods:(这个不知道怎么翻译好,就叫做始终调用的函数,因为下面的函数几乎整个游戏周期都在不断的调用,用以更新画面)

update():原文说这个方法在每一次游戏迭代中都会被调用(其实简单来说,我们游戏画面每秒60帧,每一帧都要更新画面,那么根据什么来更新画面,所谓更新画面,就是画出当前主角应该出现的地方,这个根据什么来更新?就是根据这个update方法来更新)这是这个类的核心方法,后面很多类其实都是依葫芦画瓢,跟这个原理差不多,所以这个是游戏编程中的重中之重。原作者也提醒大家一定要想明白,读清楚这个类,是游戏编程的核心,请务必认真读。


1、速度值可以为负,如果X方向上的速度为负,就说明我们的主角向左边移动。


2、之前提到过,整个游戏窗口可以看成一个坐标系,而这个坐标系的原点(0,0)在窗口的左上角,这在后面会详细说,此时,就意味着,如果Y方向的速度值是为正数,此时人物的位置并非往上升,而是往下掉。



3.这点有点多余,不过还是给不太熟悉该语言的人提个醒,+=的意思是

x+=1等价于x=x+1


4.这里,我们定义游戏的地面在第440像素(纵坐标最大值是480,所以440可以定义成是地面,也可以看成我们就是上帝,我们规定纵坐标(x,440)的点连成的线都是地面),所以,当 centerY (就是人物的中心点Y方向上的值)等于382的时候,就意味着他双脚着地了。


5.同理,centerX的值少于60的话,也就是说任务的左边肩膀碰到了最左边的边界。


至于为什么是382和60,取决于我们主角的大小,是依据主角图像像素而来,看到后面的章节你就会懂了。


现在来让我们看一张图片:



再详细说说UPDATE方法,然后再继续说明

public void update() {

		// Moves Character or Scrolls Background accordingly.
		if (speedX < 0) {
			centerX += speedX; //根据速度修改X方向上的坐标
		} else if (speedX == 0) { 
			System.out.println("Do not scroll the background.");

		} else {
			if (centerX <= 150) { //如果主角的X坐标在小于150,也就是触发屏幕滚动前可允许移动的做坐标值
				centerX += speedX; //我们就让主角移动
			} else { 
				System.out.println("Scroll Background Here"); /
			}
		}
		
		// Updates Y Position
		if (centerY + speedY >= 382) {
              //下落速度加Y方向的坐标有可能大于382,一旦大于,就固定其在地面上。
			centerY = 382;
		}else{                      
                     centerY += speedY; //Add speedY to centerY to determine its new position
                }

		// Handles Jumping
		if (jumped == true) {
			speedY += 1; //While the character is in the air, add 1 to his speedY.     
                       //NOTE: This will bring the character downwards!

			if (centerY + speedY >= 382) {
				centerY = 382;
				speedY = 0;
				jumped = false;
			}

		}

		// Prevents going beyond X coordinate of 0
		if (centerX + speedX <= 60) { //If speedX plus centerX would bring the character     //outside the screen,
			centerX = 61;
                       //Fix the character's centerX at 60 pixels.
		}
	}

剖析update方法:

由于以后有很多类,如敌人类啊,背景类里面都有update这个方法,所以必须弄懂。

1.第一个if语句段落。

为了让游戏可以描绘出当前主角的位置,我们必须时常更新centerX这个参数,如果speedX等于0,就相当于主角呆呆的站在原地没有移动。


2、请观看一下录像(由于网络原因,这里就不放了,其实就是合金弹头的游戏路线,没看过合金弹头的请自行百度)



在合金弹头这款游戏里面,主角可以在左半屏幕中只有活动,但是当他走到右边差不多一半的时候,背景就开始滚动了,实际上主角在窗口中的横坐标位置并未改变,只是背景等发生了变化,向左边移动,就有点像物理上参照物的原理,当你将运动中的人看成是静止了的话,动的则是身边的景物。同理,当速度为0,我们不滚动屏幕,当速度不为0但是横坐标的值少于150,主角就能自由活动活蹦乱跳,如果大于150,那么背景就开始滚动,主角在游戏屏幕中横坐标的值就固定在那一点了。


接着让我们谈谈横坐标。

由于重力是存在的,因此,在游戏世界中也应该有重力,我们之前也说过,我们假设地面实在纵坐标第440的位置,而且刚刚也提到过,由于原点在左上角的关系,所以Y方向上的左边加上Y方向上的速度,只会带主角回地面,这巧妙就巧妙在这,需要拐个弯。因为我们设定原点在左上角,因此,Y方向上的速度只需要设定成一个负数,那么跳起来那一刻给速度设一个正数值,由于每次-1,每秒减去60次,所以,始终都会掉在地面上的。这点需要在代码中慢慢体会出来,有点绕。


所以,当处理跳跃请求的时候,我们根据Y方向上的速度和坐标,来看看主角是不是在移动,究竟是在半空中还是在地面,毕竟我们这款游戏是不允许主角可以在半空中连跳的。


最后,我们来讨论一下横坐标。


在这段代码中,我们只是检测主角有没有出最左边的边界,因为代码中的x代表的是游戏主角的中心点而非最左边的点,结合图像本身的尺寸,我们不难得出,如果x小于60的话,主角就会出最左边的界,也就是主角的左手会超出屏幕边缘,因此,如果玩家按住左方向键让主角不断左移,当有可能移动出最左边的边界导致游戏主角消失在屏幕中的话,我们就必须做出纠正,固定主角只能停留在最左边并且保证主角不会超出界限。


以上就是我们讨论的update()方法,现在我们来讨论一下其他方法。


2.处理用户的操纵输入


大体上由以下四个方法完成

这里为了节省功夫,我就直接复制粘贴原文了。


moveRight(), which sets the character's horizontal speed (speedX) as 6.

让主角水平右移,并且速度为6.
moveLeft(), which sets the character's speedX as -6.

同理,水平左移,速度就是-6(之所以为6是因为规定了向右速度为正,向左速度为负,这样能减少一个boolean值判断左右移动的同时也能直接对坐标进行运算,是一石二鸟的方法)
stop(), which sets the speedX as zero.

停止,让速度归零。
jump(), which sets the vertical speed as -15. 

跳起,让纵向速度为-15(回想一下之前说过的,画面0点再左上角,而主角一般在下方,所以纵向速度为负其实就是跳起来了)


3.辅助方法

稍后再添加。


2-10图像知识

为了阐释速度值的含义,在此介绍一下有关图形的知识。


像素是显示屏上单位面积最小的点,他们是你屏幕上最小的显示单位,他们通过改变开关状态来改变屏幕的颜色。(这个可自行百度)

屏幕分辨率指的是水平和垂直方向上有多少个像素,例如全高清1080P指的是水平上有1920个像素点,垂直上有1080个点,由于我们选择左上角为原点(0,0),所以右下角的坐标就是(1919,1079)。


对于每一个坐标(x,y)都有唯一一个像素点与之对应,然而,由于惯例和技术的限制,我们最左上角的点设定为原点。


现在开始阐述如何在游戏中产生动态的画面(原文说是动态运动错觉,其实是一个意思,因为跟视觉残留差不多都是一种错觉)


在StartingClass里的paint方法中,我们将添加如下方法:

drawRobot(centerX,centerY)。这个方法将根据给定坐标来在画面中画机器人出来。


然后,由于游戏是每秒更新60次,所以当机器人的坐标发生改变,相应的update方法也会用paint方法来更新图像,而paint方法会用上面刚刚说的来更新主角的位置。主角位置坐标的改变则是由移动速度来决定的。


所以,当调用moveRight()这个方法的时候,主角的水平速度就会设定成是6,也就是每次位移6个像素,在我们的游戏里,update方法会将速度6加在centerX上,然后再传递给drawRobot这个方法,这个方法就会绘制我们的机器人到新的位子上,从而更新我们主角的位置。如果这一些列更新过程运行得流畅的话,我们将会看到流畅的画面移动。


创建Robot对象


我们现在可以在StaringClass类中创建一个Robot对象了。

找到:

public class StartingClass extends Applet implements Runnable, KeyListener {

在这里下方,我们添加

private Robot robot;

来创建一个实例


现在我们来初始化这个实例

在start()方法中添加

robot = new Robot();

从而初始化这个实例。


现在我们就创建好了一个Robot类的一个实例了,然后我们就可以调用里面的方法了。


2-11 在StaringClass中更新画面

在这一讲中涵盖了大量的代码,因此我会尽量深入浅出的进行讲解。然而这里有一些比较难的概念,可能需要你在日后编程的时候遇到了,再仔细深入去思考解决方法,再进行理解消化。而这里只是一个例子,这个例子容易实现但难以理解,无需你完全记忆或者明白(这是原作者的话,有点深奥,不过也不难理解,自己走一次程序就可以理解了。)


我们将在StartingClass中完成以下的工作

1.首先确保引入java.awt.Graphics;

2.我们将在run方法下(无论run方法在哪,我只是希望相关的类能够放在一起有助整理)创建以下方法。

update(Graphics g)

paint(Graphics g).

只要用自动补全的方法就可以创建好这两个方法了(或者在菜单中选择复写,前几篇已经有所介绍过这个方法,这里就不再敖述了)


update方法是自动调用的,每次循环都会被调用一次,是用来更新游戏中的实况,也就是随着游戏进程更新游戏。而paint方法也差不类似。然后在update方法中生命repaint方法。

1.定义update方法

首先我们来处理update方法。我们在这个方法中使用一种叫双重缓冲的技术。双缓冲技术是一种防止图像抖动和撕裂的一种技术,这里作者也没有再多详细说,简单来说就是把要显示的所有元素一次过整理好,预先拷贝一份在内存中,然后不断的从内从中取需要显示的图像。作者的说明就是预先先制作好需要显示的画面,然后再不断的显示出来,这样看上去游戏的动作会比较流畅一些。


1.先声明变量(在robot之下)

private Image image;
private Graphics second;

作为类变量(私有变量,整个类中的方法都可读取)


2.如果你收到错误提示,这是因为你没有引进相应的图像包,请根据提示引入即可。

3.现在我们移到update方法中添加以下代码:

@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}


		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);


		g.drawImage(image, 0, 0, this);

		
	}


这一段代码的每条语句都是很不难理解的,困难的是如何将这些代码组织起来,组成一个双缓冲系统,在这里,原文作者建议大家知道一下双缓冲以及这段代码就好,日后要用到的话就过来看就是了。


定义paint()方法

paint()方法将会帮我们更新画面,现在,我们需要在画面上显示出我们的主角,所以,我们就必须借助paint()方法了。


2-15 StartingClass.java 内的paint()方法

 @Override
	public void paint(Graphics g) {
		g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this);

	}

 g.drawImage()方法有两个参数

g.drawImage(img, x, y, observer)

第一个是图形变量,第二个就是这个图形将要显示的坐标值,第三个是图像检测器(这个再以后的课程会讲到)


在我们这个例子当中,我们将使用character这个变量来代表我们的主角的图像,然后相对于中心点(centerX, centerY),我们以中心点往左的第61个像素和往上的第61个像素作为左上角(这里其实也不难理解,我们定义机器人的位置是定义中心点,而这个显示机器人所在位置的函数是以左上角坐标作为基础坐标值来画的,所以要将中心点和左上角的左边做一个转换,读者如若不明白,可以到程序里面做测试,看看讲中心点-61和-63和不减去有什么效果)。然后我们用this来作为我们的图像观测器。


好了,上面输入完成后,你会发现程序会提示有错误,请不要慌张,我们接下去会解决这些问题。


解决错误


1.character这个变量我们尚未定义,所以,我们现在首先来定义这个变量。

首先我们找到这条语句:private Image image;

然后就在其后面定义character变量,如下:

private Image image, character;


2.现在我们还没有为其赋值,所以接下去我们要为这个变量赋值,因此,我们必须创建一个URL对象(如 C:\Images\image.jpg)来允许我们链接图片资源,为这个变量赋值。


所以,我们在 private Image image, character;添加:

private URL base;

(这里要注意,私有变量只能允许类内使用)


记得要引入URL相关的包。


3.在init()方法类,我们使用URL base这个变量为character赋值,最后更改为:

@Override
	public void init() {

		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("Q-Bot Alpha");
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}


		// Image Setups
		character = getImage(base, "data/character.png");

	}

4.最后,我们必须创建一个资源文件夹,右键点击src(项目中的src文件夹),然后在src目录下新建一个data文件夹,然后下载以下图片,添加进去(新建文件夹和添加图片都用的是编程软件完成,添加图片其实可以用鼠标直接拖入软件中的相应文件夹即可)




经过以上的步骤之后,之前编译器提示的错误应该就可以消除了,现在我们的StartingClass应该像是这个样子的:

(如果没有消除,请试试重新编译一下 On the toolbar above, click on Project > Clean > OK)


 package kiloboltgame;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;

public class StartingClass extends Applet implements Runnable, KeyListener {


        private Image image, character;
	private Graphics second;
	private URL base;

	@Override
	public void init() {

		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("Q-Bot Alpha");
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// Image Setups
		character = getImage(base, "data/character.png");

	}

	@Override
	public void start() {
		robot = new Robot();

		Thread thread = new Thread(this);
		thread.start();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}

	@Override
	public void run() {
		while (true) {

			repaint();
			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}

		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);

		g.drawImage(image, 0, 0, this);

	}

	@Override
	public void paint(Graphics g) {
		g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this);

	}

	@Override
	public void keyPressed(KeyEvent e) {

		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Move up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Move down");
			break;

		case KeyEvent.VK_LEFT:
			System.out.println("Move left");
			break;

		case KeyEvent.VK_RIGHT:
			System.out.println("Move right");
			break;

		case KeyEvent.VK_SPACE:
			System.out.println("Jump");
			break;

		}

	}

	@Override
	public void keyReleased(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Stop moving up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Stop moving down");
			break;

		case KeyEvent.VK_LEFT:
			System.out.println("Stop moving left");
			break;

		case KeyEvent.VK_RIGHT:
			System.out.println("Stop moving right");
			break;

		case KeyEvent.VK_SPACE:
			System.out.println("Stop jumping");
			break;

		}

	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

}


解决get...错误


现在我们的StartingClass其实仍有一点错误还没有完全清除。



在我之前对Robot类的描述中,我们提到过我们应该创建:

1、在每一次游戏循环中不断的更新方法

2、方法只对用户输入进行相应

3、辅助函数,用于与其他类互通变量变化的情况


我们辅助函数还没有完成,所以现在来完成


1.再次打开Robot.java

2.在任意空白地方,单击鼠标右键,选Source >> and select Generate Getters and Setters.





选择所有,然后以先getters后setters排序,点击OK。


什么事getter和setter?

因为当其他类要使用我们的内部私有变量的时候,是不能直接访问的,所以,我们这里要使用getter和setter函数来对内部私有变量进行访问。

这样来说比较安全。


他们看上去是成对出现的:


public int getSpeedX() {
return speedX;
}

and
public void setSpeedX(int speedX) {
this.height = speedX;
}


一旦getter方法被调用,它就会返回你想要的变量值,当有这样一句:

myNewVariable = getSpeedX()


它就会返回当前X方向上的速度。


StartingClass解决错误之后的样子是:

package kiloboltgame;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;

public class StartingClass extends Applet implements Runnable, KeyListener {

	private Robot robot;
	private Image image, character;
	private Graphics second;
	private URL base;

	@Override
	public void init() {

		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("Q-Bot Alpha");
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// Image Setups
		character = getImage(base, "data/character.png");

	}

	@Override
	public void start() {
		robot = new Robot();

		Thread thread = new Thread(this);
		thread.start();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}

	@Override
	public void run() {
		while (true) {

			repaint();
			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}

		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);

		g.drawImage(image, 0, 0, this);

	}

	@Override
	public void paint(Graphics g) {
		g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this);

	}

	@Override
	public void keyPressed(KeyEvent e) {

		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Move up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Move down");
			break;

		case KeyEvent.VK_LEFT:
			System.out.println("Move left");
			break;

		case KeyEvent.VK_RIGHT:
			System.out.println("Move right");
			break;

		case KeyEvent.VK_SPACE:
			System.out.println("Jump");
			break;

		}

	}

	@Override
	public void keyReleased(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Stop moving up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Stop moving down");
			break;

		case KeyEvent.VK_LEFT:
			System.out.println("Stop moving left");
			break;

		case KeyEvent.VK_RIGHT:
			System.out.println("Stop moving right");
			break;

		case KeyEvent.VK_SPACE:
			System.out.println("Stop jumping");
			break;

		}

	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

}

2-12 主角的移动


此时,你应该可以在屏幕上看到我们的主角,而且显示上应该不会出现撕裂:


现在我们要让主角给动起来,


首先,在StartingClass的keyPressed方法中,置换以下方法:


1. System.out.println("Move left"); with robot.moveLeft();
2. System.out.println("Move right"); with robot.moveRight();
3. System.out.println("Jump"); with robot.jump();


然后在keyReleased()方法中,置换以下方法

1. System.out.println("Stop moving left");

2. System.out.println("Stop moving right");
语句都改成
robot.stop();


第二:在run()方法中,我们需要调用robot.update();函数


	@Override
	public void run() {
		while (true) {
			robot.update();
			repaint();
			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}


然后你就会得出我们这一讲最后的结果了:


首先是StartingClass:

package kiloboltgame;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;

public class StartingClass extends Applet implements Runnable, KeyListener {

	private Robot robot;
	private Image image, character;
	private Graphics second;
	private URL base;

	@Override
	public void init() {

		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("Q-Bot Alpha");
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// Image Setups
		character = getImage(base, "data/character.png");

	}

	@Override
	public void start() {
		robot = new Robot();

		Thread thread = new Thread(this);
		thread.start();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}

	@Override
	public void run() {
		while (true) {
			robot.update();
			repaint();
			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}

		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);

		g.drawImage(image, 0, 0, this);

	}

	@Override
	public void paint(Graphics g) {
		g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this);

	}

	@Override
	public void keyPressed(KeyEvent e) {

		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Move up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Move down");
			break;

		case KeyEvent.VK_LEFT:
			robot.moveLeft();
			break;

		case KeyEvent.VK_RIGHT:
			robot.moveRight();
			break;

		case KeyEvent.VK_SPACE:
			robot.jump();
			break;

		}

	}

	@Override
	public void keyReleased(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("Stop moving up");
			break;

		case KeyEvent.VK_DOWN:
			System.out.println("Stop moving down");
			break;

		case KeyEvent.VK_LEFT:
			robot.stop();
			break;

		case KeyEvent.VK_RIGHT:
			robot.stop();
			break;

		case KeyEvent.VK_SPACE:
			System.out.println("Stop jumping");
			break;

		}

	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

}


完成后的Robot类:

 package kiloboltgame;

import java.awt.Graphics;

public class Robot {

	private int centerX = 100;
	private int centerY = 382;
	private boolean jumped = false;

	private int speedX = 0;
	private int speedY = 1;


	public void update() {

		// Moves Character or Scrolls Background accordingly.
		if (speedX < 0) {
			centerX += speedX;
		} else if (speedX == 0) {
			System.out.println("Do not scroll the background.");

		} else {
			if (centerX <= 150) {
				centerX += speedX;
			} else {
				System.out.println("Scroll Background Here");
			}
		}
		
		// Updates Y Position
	
		if (centerY + speedY >= 382) {
			centerY = 382;
		}else{                        
                        centerY += speedY;
                }

		// Handles Jumping
		if (jumped == true) {
			speedY += 1;

			if (centerY + speedY >= 382) {
				centerY = 382;
				speedY = 0;
				jumped = false;
			}

		}

		// Prevents going beyond X coordinate of 0
		if (centerX + speedX <= 60) {
			centerX = 61;
		}
	}

	public void moveRight() {
		speedX = 6;
	}

	public void moveLeft() {
		speedX = -6;
	}

	public void stop() {
		speedX = 0;
	}

	public void jump() {
		if (jumped == false) {
			speedY = -15;
			jumped = true;
		}

	}

	public int getCenterX() {
		return centerX;
	}

	public int getCenterY() {
		return centerY;
	}

	public boolean isJumped() {
		return jumped;
	}

	public int getSpeedX() {
		return speedX;
	}

	public int getSpeedY() {
		return speedY;
	}

	public void setCenterX(int centerX) {
		this.centerX = centerX;
	}

	public void setCenterY(int centerY) {
		this.centerY = centerY;
	}

	public void setJumped(boolean jumped) {
		this.jumped = jumped;
	}

	public void setSpeedX(int speedX) {
		this.speedX = speedX;
	}

	public void setSpeedY(int speedY) {
		this.speedY = speedY;
	}


}


第四讲到此结束,这一讲的内容非常多非常详实,所以花了我不少时间翻译,而且内容又非常的绕口,艰深,我想了很久才想到怎么翻译,所以用了比较长的时间来写这一章,啊,我要休息一下了。希望大家不会因为我太久没更新而误以为我不更新了,其实不然,这一讲的内容实在是太重要和太丰富了,而且以后的背景类、敌人类、动画类都是依葫芦画瓢,所以必须讲的丰富一点。


如果你觉得有用的话请留言支持一下,没人支持真的写不下去,太辛苦了。。。


可能排版上会有些混乱,到时候多人看之后再统一解决吧。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值