JAVA游戏入门开发DAY 2 基本架构

这一讲主要讲解线程。


为了贯彻之前说的宗旨,就是尽努力让所有人都看得懂,看得明白,所以尽可能花比较多的时间去讲解一下概念。


何为线程?这就要从计算机的结构说起。理论上说CPU的运算速度是纳秒级,但是其他部件的传输速度可能是毫秒级,这就造成的CPU经常没事等事做。


就好像计算机要算1+1可能要1秒的话,从内存中读出来可能要1000秒,也就是16分钟。


换个生活中的例子,比方说你要洗衣服,将衣服扔进去洗衣机,加洗衣粉,这个过程可能只需要一两分钟,但是洗衣机完成一次标准洗涤则需要30分钟。这时候,你会干站在洗衣机前等衣服洗好?还是做其他事情?(在这里,人可以思考,相当于CPU,内存等设备相当于洗衣机)


简而言之,就是计算机计算速度很快,但是等给他计算的数据却等得花儿都谢了,就好像用洗衣机洗衣服一样,正常来说衣服扔进去洗衣机开动之后,都回去做其他事吧?更何况是计算机。


洗衣机开动后,你可以去玩游戏,看BILIBI。随你喜欢,但是计算机呢?下一件事情做什么?如果数据同时到达,先计算哪个?这就由操作系统决定,看操作系统取那种策略,可以采取的策略有短服务优先,先来先服务,最短轮转时间,优先权,权值等等等等一大堆的方法,有兴趣可以去看操作系统理论这本书或者这门课,这里就不多说了。


其实跟我们平时做家务差不多,我们有洗衣机,有电饭煲,想必有的人是一边洗衣服,一边用电饭煲煮饭,然后炒菜,在计算机的情况也差不多。


同时,计算机的执行速度很快,而且CPU发展到可以在不同的程序之间切换,如执行一下QQ,然后跑去执行以下QQ音乐等,而且还能保证这些程序的正常运行,因为这些程序比较好执行,不太会占用CPU太多的时间去执行,所以,CPU可以在多个程序之间往来穿梭,由于CPU的切换速度很快,所以让人感觉这些程序都是同时执行,并行的,不过学术上,因为CPU只有一个,而且是来回快速切换,我们叫这种情况叫做并发执行。


然后,我们要引进两个概念,进程和线程。线程+进程=程序。进程资源管理的单位,线程是执行单位,一个程序只有一个进程,但是可以有很多线程。


举个例子来说,我们用洗衣机洗衣服,洗衣机、水、洗衣粉等这些相当于进程,而衣服相当于线程,线程可以有一个,也可以有多个。

再举个例子,比方说采矿,矿山相当于进程,只有一个,矿工相当于线程,有好多好多个。


其实这是将CPU可以在多个程序之间并发执行的这个特性,上升到一个逻辑理论,一个面向对象的逻辑。


更简单一点来说,CPU可以看成是漩涡鸣人,会影分身术,可以变出很多很多个分身,分身相当于线程。我漩涡鸣人本体本身就是一个进程。


由于CPU并发执行的特性,所以就决定了CPU可以以超级快速的速度在多个线程之间切换,给人的感觉就是计算机可以同时执行多个线程。


其实多线程是一个好东西,在特别在游戏中,如丧尸围城,一个僵尸相当于一个线程,这样就可以让电脑变出多个僵尸。在游戏编程中相当重要。


大家可能没这个体验,说一个多线程的例子,如下载,IE自带的下载其实是一个单线程的程序,只有一个线程,下载往往需要很长时间。但是如果换成多线程的话,程序可以模拟成N个用户同时下载,这样下载的速度就可以很快,如迅雷,这就是多线程的好处。多线程就可以将CPU抽象成一个电脑有多个CPU同时再运作。


好了,大致上有了多线程的概念以后,在JAVA中如何实现呢?下面正片。


@Override
public void start() {
    Thread thread = new Thread();
    thread.start();
} 

很简单对吧?函数中第一行就是新建一个线程实例,也就是告诉计算机我现在生成一个线程,第二句的意思就是线程开始执行。


然后,如果你直接这样写,却没有任何效果,为什么呢?


因为你还没跟计算机说这个线程要执行什么操作。


Thread thread = new Thread() {
    public void run() {
        for (int i = 0; i < 10; i += 2) {
            System.out.println("hello");
        }
    }

}; 

线程至少需要有一个RUN方法来告诉计算机要做什么事,如上所示。我们将让线程执行RUN里面的操作。


由于我们之前的程序已经继承了一个父类,根据JAVA的尿性,一个类只能继承一个父类,如果要用线程,我们就不能直接继承线程类,只能用实现接口的方法,实现线程类。


我们需要在类的最后面添加一个关键字 "implements Runnable" 


就好像这样

public class StartingClass extends Applet implements Runnable{...


然后我们复写run方法,你可以用上节课教的从菜单中添加run方法,或者是直接在类里面先输入run,然后按CTRL+/,让程序自动补全。


接着我们改改之前的线程声明


Thread thread = new Thread(this);


this代表本类,其实这里涉及一个Java他妈的编程方法,有点绕,简单来说我们只是在StartingClass里面实现了Runnable接口,可以理解为只是定义了一个灵魂,需要一个躯体,承载这个灵魂,只有灵魂和躯体结合,这个生物才会动,这里也一样,我们借Runnable接口来定义了一个RUN,但是这个run方法要运行,就需要借助Thread这个类了,其实台湾将类叫类别,实例叫物体(物体还是物件不太记得),就可以理解成,我们定义了一个线程类别的物体,然后向这个物体倾注灵魂。而灵魂的位置就是这个子类,也就是StartingClass,所以要在括号内填如this.因为这句声明语句也恰巧在这个类中声明(你写StaringClass好像也行,不过略微有点蛋疼罢了)。


package kiloboltgame;

import java.applet.Applet;

public class StartingClass extends Applet implements Runnable {

    @Override
    public void init() {

    }

    @Override
    public void start() {
        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() {
        // TODO Auto-generated method stub
    }
} 


然后我们现在所得结果就是这样(P.S,老外就是有耐心,一步一步按部就班的来,赞一个,这样就不会掉队了)


4.创建一个无限游戏循环。

接上节课最后说的游戏循环,在这一篇中我们将创建一个游戏循环,游戏循环是游戏的心脏,它会根据游戏的变化对游戏场景等做出相应的更新。


随着课程的深入,游戏循环会越来越复杂,但是,万变不离其宗,循环要做的工作不外乎一下几点:

1、更新主角的状态

2、更新整个游戏环境(如分数啊,碰撞啊,主角的位置和敌人的位置啊)

3、更新游戏图形图像(游戏有画面,需要不断的去更新画面,这。。玩过游戏的都知道吧?就不详细说明了)

4、让这个更新线程每17毫秒休眠一次。


为什么让线程每17毫秒休眠一次?休眠其实就是停止运行,原文作者写了很多,其实原因很简单,就是让游戏的帧数保持在60帧。

知道电视机原理的人都知道,电视机用的是视觉残留技术,其实人眼对每秒30帧的动画看上去已经很流畅,但是人眼的极限在60帧,理论上说快过每秒60帧的变化人感觉不出来。


简而言之,如果要让整个游戏画面每秒更新60次的话,这样就需要这个更新线程每17毫秒休眠一次。其实简单算下来,一秒等于1000毫秒,17毫秒休眠一次的话,那么这个线程一秒钟最快也只能更新画面60次。


现在,为了让这个线程无休止执行下去,我们添加以下语句(其实一旦开启了一个新线程,那么我们的程序现在有两个线程,一个是窗体APPLET的线程,管理窗体本身事件的,看看有没有人按退出的按钮,一个就是游戏执行的线程,管理游戏画面的,相当于两个人在同时工作,简单理解的话)


1.添加一个WHILE循环,在循环条件中写true就能让线程一直执行下去


while(true){


}


2。再在里面添加如下代码


repaint(); // this calls paint

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


最终结果就是酱紫的:

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

repaint();是一个框架自带函数,一个会调用paint方法的函数,而paint又是绘制画面的函数,至于piant我们还没创建,我们现在只是规定了每17毫秒更新一个画面。


由于线程休眠的语句有可能会出错,所以java硬性规定我们执行这个语句只能在try catch语句中执行。


try catch这个实在是太简单,不会的请自行百度,简单来说就是先执行一下try的语句,然后如果出错就抛出错误,然后当做没事继续执行。


现在我们的结果应该是这样子了

package kiloboltgame;

import java.applet.Applet;

public class StartingClass extends Applet implements Runnable {

    @Override
    public void init() {
    }

    @Override
    public void start() {
        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();
            }
        }

    }
} 


2-5,定义窗口大小,背景颜色,程序标题。


这一段就是叫你怎么在新建的窗体中设置一些必要的参数。


之前上一讲我们探讨过这四个货: init(), start(), stop(), and destroy().

他们贯穿与整个程序的执行周期,现在我们来讨论init()方法。当我们程序初次运行,init方法就会执行(就好像命令行JAVA程序中的main函数)


所以在这个方法里,我们定义三个参数

1、窗口大小

2、背景颜色

3、窗口的标题


在这个方法里,我们将添加如下语句


1. setSize(800, 480);
to change the size to 800 pixels by 480 pixels (the most common Android resolution)
(请允许我偷懒一下,直接复制粘贴)设置窗口大小为800×400(符合安卓APP的分辨率大小)
2. setBackground(Color.BLACK);
to set background with the Color Black. NOTE: Color.BLACK refers to a constant called BLACK in the Color superclass, and hence you must import Color:

这只背景颜色为黑色,注意,Color.BLACK代表黑色,是一个常量,这个常量来自于Color这个父类,所以又要导入包了。。。



3. setFocusable(true);
This statement makes sure that when the game starts, the applet takes focus and that our input goes directly into it. If this is not enabled, then you would have to click inside the applet before it starts handling keyboard events.


这个函数的意思是获得焦点,也就是程序一旦执行,键盘鼠标的输入全部都由这个程序接收,就是你按了什么键,就立即通知你写的这个程序,否则,你就得先用鼠标单击一下这个窗口,才能就接收到,这个你可以自己做个实验,将ture改成false就知道效果了


4.  Frame frame = (Frame) this.getParent().getParent();
     frame.setTitle("Q-Bot Alpha");


Here again, you must import Frame to create a Frame object called frame. This is slightly complicated, but just know that the first line assigns the applet window to the frame variable an the second line just sets the title to be Q-Bot Alpha


在这里,我们声明了一个Frame的变量,所以又要导入包包了。原文作者在此没有做太多说明,只是让你知道第一行的意思是将程序窗口画面交给frame这个变量,第二句是将标题设置一下。

今天最后得出的结果如下所示:

package kiloboltgame;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;

public class StartingClass extends Applet implements Runnable {

    @Override
    public void init() {

        setSize(800, 480);
        setBackground(Color.BLACK);
        setFocusable(true);
        Frame frame = (Frame) this.getParent().getParent();
        frame.setTitle("Q-Bot Alpha");
    }

    @Override
    public void start() {
        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();
            }
        }
    }
} 


最后作者提供了三个快捷键,其实没啥用,只需要知道Ctrl+/和第二个就好,不过翻译的时候时间已经夜深了,很困,我直接贴了,没啥用,╮(╯_╰)╭,给大家参考一下。。


Ctrl+Shift+O: Auto import code (may import the wrong classes)自动导入代码
Ctrl+Shift+F: Auto format code into proper indents格式化代码
Ctrl+Space: Open auto complete suggestions.自动完成(这个在中国不能用吧?)


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值