08 pygame編程入門實踐篇(下)

pygame編程入門之八:Making Games With Pygame2

4. 游戲?qū)ο箢?/h3>

一旦您加載了模塊,并編寫了資源處理函數(shù)依啰,您就需要繼續(xù)編寫一些游戲?qū)ο罅说暝妗_@樣做的方式相當簡單,盡管一開始看起來很復雜鸯两。你為游戲中的每一種對象編寫一個類甩卓,然后為對象創(chuàng)建這些類的實例。然后缀棍,您可以使用這些類的方法來操作對象,給對象一些動作和交互功能父腕。所以你的游戲在偽代碼中青瀑,會是這樣的:

#!/usr/bin/python

# [load modules here]

# [resource handling functions here]

class Ball:
    # [ball functions (methods) here]
    # [e.g. a function to calculate new position]
    # [and a function to check if it hits the side]

def main:
    # [initiate game environment here]

    # [create new object as instance of ball class]
    ball = Ball()

    while 1:
        # [check for user input]

        # [call ball's update function]
        ball.update()

當然,這是一個非常簡單的例子斥难,您需要輸入所有的代碼,而不是那些帶括號的注釋群扶。但是你應該有基本想法镀裤。把一個類放在一個類中暑劝,你把所有的函數(shù)都放在一個球上,包括init幕垦,它會創(chuàng)造出所有的球的屬性,然后更新智嚷,它會把球移動到它的新位置纺且,然后在這個位置上移動blitting到屏幕上稍浆。
然后您可以為所有其他的游戲?qū)ο髣?chuàng)建更多的類,然后創(chuàng)建它們的實例嫁艇,這樣您就可以在主函數(shù)和主程序循環(huán)中輕松地處理它們步咪。與此形成對比的是益楼,在主函數(shù)中啟動球猾漫,然后有許多無類的函數(shù)來操作一個集合球?qū)ο螅銓吹綖槭裁词褂妙愂且粋€優(yōu)勢:它允許你把每個對象的所有代碼放在一個地方;它使用對象更容易;它添加新對象和操作它們變得更加靈活粒督。
您可以簡單地為每個新球?qū)ο髣?chuàng)建球類的新實例禽翼,而不是為每個新球?qū)ο筇砑痈嗟拇a闰挡。魔法!

4.1. 一個簡單的球類

這里有一個簡單的類,它具有創(chuàng)建球?qū)ο笏匦璧墓δ苤埃绻谥鞒绦蛑姓{(diào)用update函數(shù)花枫,那么就可以在屏幕上移動:

class Ball(pygame.sprite.Sprite):
    """A ball that will move across the screen
    Returns: ball object
    Functions: update, calcnewpos
    Attributes: area, vector"""

    def __init__(self, vector):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('ball.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.vector = vector

    def update(self):
        newpos = self.calcnewpos(self.rect,self.vector)
        self.rect = newpos

    def calcnewpos(self,rect,vector):
        (angle,z) = vector
        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
        return rect.move(dx,dy)

這里我們有球類,init球函數(shù)集,更新函數(shù),改變了球的矩形在新的位置,和calcnewpos函數(shù)計算出球的新位置根據(jù)其當前位置,移動和向量劳翰。我馬上就會解釋物理。
另一件需要注意的事情是文檔字符串乙墙,這段時間稍微長一點生均,并解釋了類的基礎(chǔ)知識马胧。這些字符串不僅對您自己和其他程序員來說很方便,而且還可以用于解析代碼并記錄代碼的工具蛙粘。它們不會對程序產(chǎn)生很大的影響威彰,但是對于大的程序來說它們是無價的,所以這是一個很好的習慣舔痕。

4.1.1. Diversion 1: Sprites

為每個對象創(chuàng)建類的另一個原因是精靈。你在游戲中渲染的每一個圖像都是一個精靈對象盈咳,因此边翼,首先组底,每個對象的類都應該繼承精靈類。這是Python類繼承的一個很好的特性〗酰現(xiàn)在厌均,球類擁有所有與Sprite類一起的功能棺弊,并且球類的任何對象實例都將被Pygame注冊為精靈。而對于文本和背景稻艰,它們不移動侈净,可以把對象放在背景上,Pygame以不同的方式處理精靈對象元扔,當我們查看整個程序的代碼時旋膳,你會看到它溺忧。
基本上盯孙,你為那個球創(chuàng)建一個球?qū)ο蠛鸵粋€精靈對象,然后你在sprite對象上調(diào)用球的更新函數(shù)歌溉,從而更新精靈痛垛。精靈還提供了復雜的方法來確定兩個物體是否相撞。通常情況下漫谷,您可能只是在主循環(huán)中檢查它們的矩形是否重疊蹂析,但這將涉及到大量的代碼,這將是一種浪費惕稻,因為Sprite類提供了兩個功能(spritecollide and groupcollide)來為您完成這項工作蝙叛。

4.1.2. Diversion 2: Vector physics

除了球類的結(jié)構(gòu)外借帘,這段代碼值得注意的是矢量物理,用來計算球的運動宋梧。任何涉及到角運動的游戲狰挡,除非你熟悉三角學加叁,否則你不會走太遠,所以我將介紹一些你需要知道的基礎(chǔ)知識來理解calcnewpos函數(shù)展融。
首先豫柬,你會注意到球有一個屬性向量烧给,它是由角和z組成的,這個角是用弧度來表示的指么,它會告訴你球運動的方向。Z是球運動的速度晚唇。所以通過這個向量盗似,我們可以確定球的方向和速度赫舒,以及它在x軸和y軸上的移動程度:

../_images/tom_radians.png

上面的圖表說明了向量背后的基本數(shù)學。
在左手圖中并鸵,你可以看到球的投影運動是由藍線表示的园担。這條線的長度(z)表示它的速度枯夜,角度是它移動的方向。球運動的角度總是從右邊的x軸上取下咏闪,從這條線順時針方向測量鸽嫂,如圖所示征讲。
從球的角度和速度,我們可以算出它沿x軸和y軸移動了多少癣籽。因為Pygame不支持向量本身滤祖,我們只能通過沿著兩個軸移動它的矩形來移動球匠童。所以我們需要在x軸(dx)和y軸(dy)上解決這個角度和速度。這是一個簡單的三角學問題楞遏,可以用圖中所示的公式來完成首昔。
如果你以前學過基本的三角學知識勒奇,這對你來說都不應該是新聞。但是格二,為了防止健忘竣蹦,這里有一些有用的公式可以記住顶猜,這將幫助你對角度進行視覺化(用度來表示角度比弧度更直觀)。

../_images/tom_formulae.png

5. User-controllable objects

到目前為止痘括,你可以創(chuàng)建一個Pygame窗口长窄,并渲染一個可以在屏幕上運行的球。
下一步是制造一些用戶可以控制的球拍纲菌。這可能比球簡單得多挠日,因為它不需要物理(除非你的用戶控制的對象會以比上下更復雜的方式移動,比如像馬里奧這樣的平臺角色嚣潜,在這種情況下你需要更多的物理知識)。用戶控制的對象很容易創(chuàng)建椅贱,這要歸功于Pygame的事件隊列系統(tǒng)懂算,正如您將看到的。

5.1. 一個簡單的球拍類

球拍類的原理與球類相似庇麦。你需要一個init函數(shù)來初始化這個球(這樣你就可以為每只球拍創(chuàng)建一個對象實例)犯犁,一個更新函數(shù),在它被擊到屏幕之前女器,在球棒上執(zhí)行每幀的變化酸役,以及定義這個類實際要做什么的功能。下面是一些示例代碼:

class Bat(pygame.sprite.Sprite):
    """Movable tennis 'bat' with which one hits the ball
    Returns: bat object
    Functions: reinit, update, moveup, movedown
    Attributes: which, speed"""

    def __init__(self, side):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('bat.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.side = side
        self.speed = 10
        self.state = "still"
        self.reinit()

    def reinit(self):
        self.state = "still"
        self.movepos = [0,0]
        if self.side == "left":
            self.rect.midleft = self.area.midleft
        elif self.side == "right":
            self.rect.midright = self.area.midright

    def update(self):
        newpos = self.rect.move(self.movepos)
        if self.area.contains(newpos):
            self.rect = newpos
        pygame.event.pump()

    def moveup(self):
        self.movepos[1] = self.movepos[1] - (self.speed)
        self.state = "moveup"

    def movedown(self):
        self.movepos[1] = self.movepos[1] + (self.speed)
        self.state = "movedown"

正如你所看到的驾胆,這個類與它的結(jié)構(gòu)中的球類非常相似涣澡。
但是每個函數(shù)的作用是不同的。首先丧诺,有一個reinit函數(shù)入桂,它在回合結(jié)束時使用,而bat需要被設置回它的起始位置驳阎,任何屬性都被設置回它們的必要值抗愁。
接下來馁蒂,球拍移動的方式比球要復雜一些,因為它的運動很簡單(向上/向下)蜘腌,但它依賴于使用者告訴它移動沫屡,不像球在每一幀中不斷移動。為了理解球的運動方式撮珠,看一個快速的圖來顯示事件的順序是很有幫助的:

../_images/tom_event-flowchart.png

這里發(fā)生的是控制球棒的人按下按鈕沮脖,將球棒向上移動。主游戲循環(huán)的每個迭代(每一幀),關(guān)鍵是是否進行,球拍的狀態(tài)屬性對象被設置為“移動”,moveup函數(shù)將調(diào)用,導致球的y位置降低速度屬性的值(在本例中,10)芯急。換句話說勺届,只要鍵盤被壓住,球拍就會以每幀10個像素的速度向上移動屏幕娶耍。state屬性還沒有使用免姿,但是在處理自旋還是想要一些有用的調(diào)試輸出,也是很有用的榕酒。
一旦玩家過去,第二組框被調(diào)用,球拍的狀態(tài)屬性對象將回到“靜止”狀態(tài),和movepos屬性將回到(0,0),這意味著當更新函數(shù)被調(diào)用時,它不會把球拍移動养泡。所以當玩家松開按鍵時,球拍就會停止移動奈应。簡單!

5.1.1. Diversion 3: Pygame events

那么我們怎么知道玩家什么時候把按鍵按下澜掩,然后釋放呢
有了Pygame事件隊列系統(tǒng),年青人杖挣!這是一個非常容易使用和理解的系統(tǒng)肩榕,所以這不會花很長時間:)您已經(jīng)在基本的Pygame程序中看到了事件隊列,它用于檢查用戶是否退出了應用程序惩妇。移動球拍的代碼就這么簡單:

for event in pygame.event.get():
    if event.type == QUIT:
        return
    elif event.type == KEYDOWN:
        if event.key == K_UP:
            player.moveup()
        if event.key == K_DOWN:
            player.movedown()
    elif event.type == KEYUP:
        if event.key == K_UP or event.key == K_DOWN:
            player.movepos = [0,0]
            player.state = "still"

這里假設您已經(jīng)創(chuàng)建了一個bat的實例株汉,并調(diào)用了object player。
您可以看到熟悉結(jié)構(gòu)布局歌殃,它遍歷Pygame事件隊列中每個事件乔妈,并用event.get()函數(shù)檢索。當用戶點擊按鍵氓皱,按下鼠標按鈕并移動操縱桿時路召,這些動作會被注入到Pygame事件隊列中,然后直到處理波材。
所以在主游戲循環(huán)的每次迭代中股淡,你都要經(jīng)歷這些事件,檢查它們是否是你想要處理的廷区,然后適當?shù)靥幚硭鼈兾椤T谇蚺纳砩系氖录ump()函數(shù)。在每次迭代中調(diào)用update函數(shù)保持隊列流隙轻。
首先埠帕,我們檢查用戶是否退出了程序垢揩,如果他們退出了,就退出敛瓷。然后我們檢查是否有任何鍵被按下叁巨,如果是,我們檢查它們是否是移動球拍的指定鍵琐驴。如果是,然后調(diào)用對應移動功能,并設置適當?shù)腷at狀態(tài)(盡管 moveup movedown改變了moveup()和movedown()函數(shù),這使得簡潔的代碼,并且不破壞封裝,這意味著您將屬性分配給對象本身,沒有引用該對象的實例的名稱)俘种。
注意這里我們有三個狀態(tài): still, moveup, and movedown秤标。同樣绝淡,如果您想要調(diào)試或計算旋轉(zhuǎn),這些都是很方便的苍姜。我們還會檢查是否有任何鍵被“松開”(即不再被按桌谓汀),如果是衙猪,我們就會阻止球拍移動馍乙。

6. 把它們放在一起

到目前為止,您已經(jīng)學習了構(gòu)建簡單游戲所需的所有基礎(chǔ)知識垫释。您應該了解如何創(chuàng)建Pygame對象丝格,Pygame如何顯示對象,如何處理事件棵譬,以及如何使用物理將一些動作引入到您的游戲中显蝌。
現(xiàn)在,我將展示如何將所有這些代碼塊放到游戲中订咸。首先要做的是讓球觸到屏幕的兩側(cè)曼尊,讓球棒能夠擊球,否則就不會有太多的比賽了脏嚷。我們用Pygame的碰撞方法來做這個骆撇。

6.1. 讓球擊中兩邊

讓它在兩側(cè)彈跳的基本原理很容易理解。你利用球的四個角坐標父叙,檢查它們是否與屏幕邊緣的x或y坐標相對應神郊。如果右上角和左上角都有y坐標為0,你就知道這個球現(xiàn)在在屏幕的最上面趾唱。在我們計算出了球的新位置之后,我們在更新函數(shù)中做了所有這些屿岂。

if not self.area.contains(newpos):
      tl = not self.area.collidepoint(newpos.topleft)
      tr = not self.area.collidepoint(newpos.topright)
      bl = not self.area.collidepoint(newpos.bottomleft)
      br = not self.area.collidepoint(newpos.bottomright)
      if tr and tl or (br and bl):
              angle = -angle
      if tl and bl:
              self.offcourt(player=2)
      if tr and br:
              self.offcourt(player=1)

self.vector = (angle,z)

檢查這個區(qū)域是否包含了球的新位置(它總是應該的,我們不需要有else子句鲸匿,盡管在其他情況下你可能想要考慮它爷怀。)
然后檢查四個角的坐標是否與該區(qū)域的邊發(fā)生碰撞,并為每個結(jié)果創(chuàng)建對象带欢。如果是的話运授,對象的值是1烤惊,或者是真值。如果不吁朦,那么價值將是零柒室,或者是假的。
然后我們看它是否擊中了頂部或底部逗宜,如果是雄右,它改變了球的方向。使用弧度纺讲,我們可以簡單地改變它的正/負的值來做到這一點擂仍。還會檢查球是否從側(cè)面消失了,如果它有的話熬甚,我們會調(diào)用offcourt函數(shù)逢渔。在游戲中,重新設置球乡括,在調(diào)用該函數(shù)時指定的玩家的分數(shù)增加1點肃廓,并顯示新分數(shù)。
最后诲泌,根據(jù)新的角度重新編譯向量盲赊。就這樣。球?qū)g快地從墻上彈回來敷扫,并以優(yōu)雅的姿態(tài)離開墻面哀蘑。

6.2. 讓球碰到球拍

把球打到球拍身上很類似,它會撞到屏幕的兩側(cè)呻澜。仍然使用碰撞法递礼,但是這次要檢查球的矩形和球拍是否碰撞。在這段代碼中羹幸,還添加了一些額外的代碼來避免各種故障脊髓。您會發(fā)現(xiàn),為了避免出現(xiàn)小故障和bug栅受,您必須添加各種額外的代碼将硝,因此習慣了它是一件好事。

else:
    # Deflate the rectangles so you can't catch a ball behind the bat
    player1.rect.inflate(-3, -3)
    player2.rect.inflate(-3, -3)

    # Do ball and bat collide?
    # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
    # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
    # bat, the ball reverses, and is still inside the bat, so bounces around inside.
    # This way, the ball can always escape and bounce away cleanly
    if self.rect.colliderect(player1.rect) == 1 and not self.hit:
        angle = math.pi - angle
        self.hit = not self.hit
    elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
        angle = math.pi - angle
        self.hit = not self.hit
    elif self.hit:
        self.hit = not self.hit
self.vector = (angle,z)

用另一段語句開始這部分屏镊,因為這是前面的代碼塊中執(zhí)行的依疼,以檢查球是否碰到了邊。
如果它沒有擊中兩邊而芥,它可能會擊中一個球棒律罢,所以繼續(xù)進行條件。第一個故障修復是在這兩個維度縮小球員矩形3像素,停止背后的球拍抓球(如果你想象你只是把球拍這球跟蹤,矩形重疊,所以通常球?qū)⒈弧按驌簟?。
接下來檢查這些矩形是否會發(fā)生碰撞误辑,還有一個小故障沧踏。請注意,我已經(jīng)對這些奇怪的代碼進行了注釋——對于那些查看代碼的人來說巾钉,解釋一些不尋常的代碼總是好的翘狱,因此當看到它的時候,您就會理解它砰苍。如果沒有修復潦匈,球可能會擊中球棒的一角,改變方向赚导,一幀后仍然會發(fā)現(xiàn)自己在球拍內(nèi)茬缩。然后它會認為它再次被擊中了,并改變了它的方向辟癌。這種情況可能會發(fā)生幾次寒屯,使得球的運動完全不真實荐捻。
所以我們有一個變量黍少,self.click,當它被擊中時处面,我們將它設置為True厂置,然后在后面加上一個False。當我們檢查這些矩形是否發(fā)生碰撞時魂角,我們也檢查是否self命中是true/false昵济,以阻止內(nèi)部的反彈。
這里的代碼很容易理解野揪。所有矩形都有一個碰撞函數(shù)访忿,你可以在其中輸入另一個物體的矩形,如果這些矩形是重疊的斯稳,如果不是海铆,它就會返回True。我們可以通過從pi中減去當前的角度來改變方向(同樣挣惰,你可以用弧度來做一個簡單轉(zhuǎn)變卧斟,它會把角度調(diào)整90度,然后把它往正確的方向發(fā)送;你可能會發(fā)現(xiàn)憎茂,在這一點上珍语,對弧度的徹底理解是有道理的!)為了完成故障檢查竖幔,我們換了self.hit板乙。如果們被擊中后的框架,那就返回False拳氢。
然后重新編譯這個向量募逞。當然晓猛,您希望刪除前一段代碼中的同一行,這樣您只需要在if-else條件語句之后才做一次凡辱。這是它!合并后的代碼將允許球擊中兩側(cè)和球拍戒职。

6.3. 成品

最終的產(chǎn)品,加上所有的代碼塊透乾,以及其他一些代碼將它們整合在一起洪燥,看起來就像這樣:

#
# Tom's Pong
# A simple pong game with realistic physics and AI
# http://www.tomchance.uklinux.net/projects/pong.shtml
#
# Released under the GNU General Public License

VERSION = "0.4"

try:
    import sys
    import random
    import math
    import os
    import getopt
    import pygame
    from socket import *
    from pygame.locals import *
except ImportError, err:
    print "couldn't load module. %s" % (err)
    sys.exit(2)

def load_png(name):
    """ Load image and return image object"""
    fullname = os.path.join('data', name)
    try:
        image = pygame.image.load(fullname)
        if image.get_alpha is None:
            image = image.convert()
        else:
            image = image.convert_alpha()
    except pygame.error, message:
        print 'Cannot load image:', fullname
        raise SystemExit, message
    return image, image.get_rect()

class Ball(pygame.sprite.Sprite):
    """A ball that will move across the screen
    Returns: ball object
    Functions: update, calcnewpos
    Attributes: area, vector"""

    def __init__(self, (xy), vector):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('ball.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.vector = vector
        self.hit = 0

    def update(self):
        newpos = self.calcnewpos(self.rect,self.vector)
        self.rect = newpos
        (angle,z) = self.vector

        if not self.area.contains(newpos):
            tl = not self.area.collidepoint(newpos.topleft)
            tr = not self.area.collidepoint(newpos.topright)
            bl = not self.area.collidepoint(newpos.bottomleft)
            br = not self.area.collidepoint(newpos.bottomright)
            if tr and tl or (br and bl):
                angle = -angle
            if tl and bl:
                #self.offcourt()
                angle = math.pi - angle
            if tr and br:
                angle = math.pi - angle
                #self.offcourt()
        else:
            # Deflate the rectangles so you can't catch a ball behind the bat
            player1.rect.inflate(-3, -3)
            player2.rect.inflate(-3, -3)

            # Do ball and bat collide?
            # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
            # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
            # bat, the ball reverses, and is still inside the bat, so bounces around inside.
            # This way, the ball can always escape and bounce away cleanly
            if self.rect.colliderect(player1.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.hit:
                self.hit = not self.hit
        self.vector = (angle,z)

    def calcnewpos(self,rect,vector):
        (angle,z) = vector
        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
        return rect.move(dx,dy)

class Bat(pygame.sprite.Sprite):
    """Movable tennis 'bat' with which one hits the ball
    Returns: bat object
    Functions: reinit, update, moveup, movedown
    Attributes: which, speed"""

    def __init__(self, side):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('bat.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.side = side
        self.speed = 10
        self.state = "still"
        self.reinit()

    def reinit(self):
        self.state = "still"
        self.movepos = [0,0]
        if self.side == "left":
            self.rect.midleft = self.area.midleft
        elif self.side == "right":
            self.rect.midright = self.area.midright

    def update(self):
        newpos = self.rect.move(self.movepos)
        if self.area.contains(newpos):
            self.rect = newpos
        pygame.event.pump()

    def moveup(self):
        self.movepos[1] = self.movepos[1] - (self.speed)
        self.state = "moveup"

    def movedown(self):
        self.movepos[1] = self.movepos[1] + (self.speed)
        self.state = "movedown"


def main():
    # Initialise screen
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption('Basic Pong')

    # Fill background
    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((0, 0, 0))

    # Initialise players
    global player1
    global player2
    player1 = Bat("left")
    player2 = Bat("right")

    # Initialise ball
    speed = 13
    rand = ((0.1 * (random.randint(5,8))))
    ball = Ball((0,0),(0.47,speed))

    # Initialise sprites
    playersprites = pygame.sprite.RenderPlain((player1, player2))
    ballsprite = pygame.sprite.RenderPlain(ball)

    # Blit everything to the screen
    screen.blit(background, (0, 0))
    pygame.display.flip()

    # Initialise clock
    clock = pygame.time.Clock()

    # Event loop
    while 1:
        # Make sure game doesn't run at more than 60 frames per second
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == QUIT:
                return
            elif event.type == KEYDOWN:
                if event.key == K_a:
                    player1.moveup()
                if event.key == K_z:
                    player1.movedown()
                if event.key == K_UP:
                    player2.moveup()
                if event.key == K_DOWN:
                    player2.movedown()
            elif event.type == KEYUP:
                if event.key == K_a or event.key == K_z:
                    player1.movepos = [0,0]
                    player1.state = "still"
                if event.key == K_UP or event.key == K_DOWN:
                    player2.movepos = [0,0]
                    player2.state = "still"

        screen.blit(background, ball.rect, ball.rect)
        screen.blit(background, player1.rect, player1.rect)
        screen.blit(background, player2.rect, player2.rect)
        ballsprite.update()
        playersprites.update()
        ballsprite.draw(screen)
        playersprites.draw(screen)
        pygame.display.flip()


if __name__ == '__main__': main()

除了展示最終產(chǎn)品,我還會把你們帶回到TomPong上乳乌,所有這些都是基于此的捧韵。
下載,看看源代碼,你會看到一個全面實施pong使用的所有代碼。在本教程中,您看到的以及很多其他的代碼我已經(jīng)添加各種版本,比如一些額外的物理旋轉(zhuǎn),和其他各種錯誤和故障修復汉操。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末再来,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子磷瘤,更是在濱河造成了極大的恐慌芒篷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件采缚,死亡現(xiàn)場離奇詭異针炉,居然都是意外死亡,警方通過查閱死者的電腦和手機扳抽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門篡帕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贸呢,你說我怎么就攤上這事镰烧。” “怎么了楞陷?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵怔鳖,是天一觀的道長。 經(jīng)常有香客問我猜谚,道長败砂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任魏铅,我火速辦了婚禮昌犹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘览芳。我一直安慰自己斜姥,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铸敏,像睡著了一般缚忧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杈笔,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天闪水,我揣著相機與錄音,去河邊找鬼蒙具。 笑死球榆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的禁筏。 我是一名探鬼主播持钉,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼篱昔!你這毒婦竟也來了每强?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤州刽,失蹤者是張志新(化名)和其女友劉穎空执,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怀伦,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡脆烟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年山林,在試婚紗的時候發(fā)現(xiàn)自己被綠了房待。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡驼抹,死狀恐怖桑孩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情框冀,我是刑警寧澤流椒,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站明也,受9級特大地震影響宣虾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜温数,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一绣硝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撑刺,春花似錦鹉胖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挠铲。三九已至,卻和暖如春寂诱,著一層夾襖步出監(jiān)牢的瞬間拂苹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工痰洒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留醋寝,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓带迟,卻偏偏與公主長得像音羞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仓犬,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容