python球球大作戰簡易版詳解

在玩很多遊戲的時候,我們可以發現遊戲裏面的世界很大,但是整個窗口卻最大不過我們屏幕大小,爲了觀察到整個世界,我們的視角窗口就會隨着裏面人物的移動不斷的移動。

比如說遊戲球球大作戰,在玩這款遊戲的時候我們會發現,我們的視角中心始終是我們的所有球,隨着球的不斷移動,我們的視角也不斷的在整個球球世界裏面移動。

對於這樣的窗口視角的移動,我們可以選擇增加兩個變量,camerax,cameray,用於記錄我們的窗口頂點。那麼什麼是窗口頂點呢?

pygame窗口移動講解:

在這裏插入圖片描述
如上圖所示,我們的窗口是圖中的小矩形,整個世界是圖中的大矩形。

我們可以將小矩形的左上角設爲(camerax,cameray),假設圖中小綠球的世界座標是(xpos,ypos),那麼在我們窗口中顯示的位置就是 (xpos-camerax,ypos-cameray) 。所以,我們可以設置一系列的世界座標,然後所需顯示在屏幕中的位置都可以和(camerax,cameray)座標進行轉換。每次移動將(camerax,cameray)進行更新便可以一直使小球顯示在窗口中。

在有了窗口移動的基礎上,我們就可以編寫我們的球球大作戰了。

簡易版球球大作戰實例:

遊戲規則:
整個遊戲對於我們的小球球而言設置了四個方向,上下左右。同時還有一個分裂的技能。球球可以靠喫食物和其他的小球獲得體積的增長,喫完所有小球或者重量到達10000獲得勝利。被其他小球吃了便失敗。

編程實現:
在這個遊戲中,我設置了三個類,分別用於存儲自己的小球類Ball,食物類Food,其他球類OtherBall。

和之前的貪喫蛇程序一樣,在最開始我們設置了一系列的遊戲常量

#定義窗口變量
WORLDWIDTH = 1500  #世界寬度
WORLDHEIGHT = 1500  #世界高度
HALFWWIDTH = int(WORLDWIDTH//2)
HALFWHEIGHT = int(WORLDHEIGHT//2)
WIDTH = 800  #窗口寬度
HEIGHT = 500  #窗口高度
CENTERWIDTH = int(WIDTH//2)
CENTERHEIGHT = int(HEIGHT//2)
FPS = 30  #幀率
SPLITBASE = 1.01 #分裂基數
pi = math.pi
INITIALWEIGHT = 20  #初始重量

#定義顏色變量
LIGHTBLACK = (10,51,71)
LIGHTBLUE = (51,102,205)
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)

#定義方向變量
UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'

在設置了這些變量之後,定義了三個類,同時在類中設置了一些方法。

1、球球類:

#球球類
class Ball():
    def __init__(self,xpos,ypos,weight,color): #定義球的x,y,重量,顏色
        self.xpos = xpos
        self.ypos = ypos
        self.radius = weightToRadius(weight)
        self.weight = weight
        self.speed = weightToSpeed(weight)
        self.color = color

    def move(self,direction):  #小球移動
        rec = pygame.Rect(-HALFWWIDTH, -HALFWHEIGHT, WORLDWIDTH, WORLDHEIGHT)
        if direction == UP:
            if rec.top < self.ypos - self.radius: #限制在上邊界以下
                self.ypos -= int(self.speed//20)
        elif direction == DOWN:
            if rec.bottom > self.ypos +self.radius:
                self.ypos += int(self.speed//20)
        elif direction == RIGHT:
            if rec.right > self.xpos + self.radius:
                self.xpos += int(self.speed//20)
        elif direction == LEFT:
            if rec.left < self.xpos - self.radius:
                self.xpos -= int(self.speed//20)

    def split(self,direction): #分裂小球函數
        newweight = math.floor((self.weight // 2) * SPLITBASE)
        newball = Ball(self.xpos, self.ypos, newweight, self.color)
        if direction == UP:
            #分裂流暢動畫
            for i in range(10):
                newball.ypos -= round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == DOWN:
            for i in range(10):
                newball.ypos += round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == LEFT:
            for i in range(10):
                newball.xpos -= round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == RIGHT:
            for i in range(10):
                newball.xpos += round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        self.setWeight(newweight) #分裂完後設置球的重量
        selfBalls.append(newball)

    def setWeight(self,newweight):
        self.weight = newweight
        self.speed = weightToSpeed(newweight)
        self.radius = weightToRadius(newweight)

    def eatFood(self): #喫食物
        global foodlist
        selfworldx = self.xpos
        selfworldy = self.ypos
        for food in foodlist:
            distance = math.sqrt((selfworldx-food.xpos)*(selfworldx-food.xpos)+(selfworldy-food.ypos)*(selfworldy-food.ypos))
            if distance < self.radius:
                self.setWeight(self.weight+food.weight)
                foodlist.remove(food)

在球球類中,我設置了移動,喫食物,分裂,更新重量等方法

2、食物類

#食物類
class Food():
    def __init__(self,xpos,ypos,weight,color,radius):
        self.xpos = xpos
        self.ypos = ypos
        self.weight = weight
        self.color = color
        self.radius = radius

食物類我就簡單設置了一些變量

3、其他球球類

#其他球類
class OtherBall():
    def __init__(self,xpos,ypos,weight,color):
        self.xpos = xpos
        self.ypos = ypos
        self.weight = weight
        self.radius = weightToRadius(weight)
        self.speed = weightToSpeed(weight)
        self.color = color
        self.direction = random.uniform(0,2*pi) #方向角度

    def eatFood(self):
        global foodlist
        for food in foodlist:
            distance = math.sqrt((self.xpos-food.xpos)**2+(self.ypos-food.ypos)**2)
            if distance < self.radius:
                self.setWeight(self.weight+food.weight)
                foodlist.remove(food)

    def setWeight(self,newweight):
        self.weight = newweight
        self.speed = weightToSpeed(newweight)
        self.radius = weightToRadius(newweight)

    def move(self):#使小球能在方框內移動
        rec = pygame.Rect(-HALFWWIDTH,-HALFWHEIGHT,WORLDWIDTH,WORLDHEIGHT)
        if rec.left>self.xpos:
            self.direction = pi - self.direction
            self.xpos += self.speed//10 #之前沒有這句,小球在碰撞幾次牆壁之後就會跳動着出界
        if rec.right<self.xpos:
            self.direction = pi - self.direction
            self.xpos -= self.speed//10
        if rec.top >self.ypos:
            self.direction = 2*pi-self.direction
            self.ypos += self.speed//10
        if rec.bottom < self.ypos:
            self.direction = 2*pi-self.direction
            self.ypos -= self.speed//10
        self.xpos += math.floor(math.cos(self.direction)*self.speed//40)
        self.ypos -= math.floor(math.sin(self.direction)*self.speed//40)

所有代碼:
那就直接上代碼吧

import pygame,random,sys,time,math
from pygame.locals import *

#定義窗口變量
WORLDWIDTH = 1500  #世界寬度
WORLDHEIGHT = 1500  #世界高度
HALFWWIDTH = int(WORLDWIDTH//2)
HALFWHEIGHT = int(WORLDHEIGHT//2)
WIDTH = 800  #窗口寬度
HEIGHT = 500  #窗口高度
CENTERWIDTH = int(WIDTH//2)
CENTERHEIGHT = int(HEIGHT//2)
FPS = 30  #幀率
SPLITBASE = 1.01 #分裂基數
pi = math.pi
INITIALWEIGHT = 20  #初始重量

#定義顏色變量
LIGHTBLACK = (10,51,71)
LIGHTBLUE = (51,102,205)
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)

#定義方向變量
UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'


#定義球球類
class Ball():
    def __init__(self,xpos,ypos,weight,color): #定義球的x,y,重量,顏色
        self.xpos = xpos
        self.ypos = ypos
        self.radius = weightToRadius(weight)
        self.weight = weight
        self.speed = weightToSpeed(weight)
        self.color = color

    def move(self,direction):  #小球移動
        rec = pygame.Rect(-HALFWWIDTH, -HALFWHEIGHT, WORLDWIDTH, WORLDHEIGHT)
        if direction == UP:
            if rec.top < self.ypos - self.radius: #限制在上邊界以下
                self.ypos -= int(self.speed//20)
        elif direction == DOWN:
            if rec.bottom > self.ypos +self.radius:
                self.ypos += int(self.speed//20)
        elif direction == RIGHT:
            if rec.right > self.xpos + self.radius:
                self.xpos += int(self.speed//20)
        elif direction == LEFT:
            if rec.left < self.xpos - self.radius:
                self.xpos -= int(self.speed//20)

    def split(self,direction): #分裂小球函數
        newweight = math.floor((self.weight // 2) * SPLITBASE)
        newball = Ball(self.xpos, self.ypos, newweight, self.color)
        if direction == UP:
            #分裂流暢動畫
            for i in range(10):
                newball.ypos -= round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == DOWN:
            for i in range(10):
                newball.ypos += round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == LEFT:
            for i in range(10):
                newball.xpos -= round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        elif direction == RIGHT:
            for i in range(10):
                newball.xpos += round(0.2*self.radius)
                drawBall(newball)
                pygame.display.update()
        self.setWeight(newweight) #分裂完後設置球的重量
        selfBalls.append(newball)

    def setWeight(self,newweight):
        self.weight = newweight
        self.speed = weightToSpeed(newweight)
        self.radius = weightToRadius(newweight)

    def eatFood(self): #喫食物
        global foodlist
        selfworldx = self.xpos
        selfworldy = self.ypos
        for food in foodlist:
            distance = math.sqrt((selfworldx-food.xpos)*(selfworldx-food.xpos)+(selfworldy-food.ypos)*(selfworldy-food.ypos))
            if distance < self.radius:
                self.setWeight(self.weight+food.weight)
                foodlist.remove(food)

#食物類
class Food():
    def __init__(self,xpos,ypos,weight,color,radius):
        self.xpos = xpos
        self.ypos = ypos
        self.weight = weight
        self.color = color
        self.radius = radius

#其他球類
class OtherBall():
    def __init__(self,xpos,ypos,weight,color):
        self.xpos = xpos
        self.ypos = ypos
        self.weight = weight
        self.radius = weightToRadius(weight)
        self.speed = weightToSpeed(weight)
        self.color = color
        self.direction = random.uniform(0,2*pi) #方向角度

    def eatFood(self):
        global foodlist
        for food in foodlist:
            distance = math.sqrt((self.xpos-food.xpos)**2+(self.ypos-food.ypos)**2)
            if distance < self.radius:
                self.setWeight(self.weight+food.weight)
                foodlist.remove(food)

    def setWeight(self,newweight):
        self.weight = newweight
        self.speed = weightToSpeed(newweight)
        self.radius = weightToRadius(newweight)

    def move(self):#使小球能在方框內移動
        rec = pygame.Rect(-HALFWWIDTH,-HALFWHEIGHT,WORLDWIDTH,WORLDHEIGHT)
        if rec.left>self.xpos:
            self.direction = pi - self.direction
            self.xpos += self.speed//10 #之前沒有這句,小球在碰撞幾次牆壁之後就會跳動着出界
        if rec.right<self.xpos:
            self.direction = pi - self.direction
            self.xpos -= self.speed//10
        if rec.top >self.ypos:
            self.direction = 2*pi-self.direction
            self.ypos += self.speed//10
        if rec.bottom < self.ypos:
            self.direction = 2*pi-self.direction
            self.ypos -= self.speed//10
        self.xpos += math.floor(math.cos(self.direction)*self.speed//40)
        self.ypos -= math.floor(math.sin(self.direction)*self.speed//40)

def main():
    global FPSCLOCK,DISPLAYSURF,cameray,camerax,selfBalls,otherBalls,foodlist,rec #設置全局變量
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF = pygame.display.set_mode((WIDTH,HEIGHT))

    camerax = -CENTERWIDTH
    cameray = -CENTERHEIGHT
    dirction = ''

    #定義小球列表
    selfBalls = []
    otherBalls = []
    foodlist = []

    for i in range(5): #創建其他小球
        xpos = random.randint(-HALFWWIDTH, HALFWWIDTH)
        ypos = random.randint(-HALFWHEIGHT, HALFWHEIGHT)
        otherb = OtherBall(xpos,ypos,INITIALWEIGHT,randomColor())
        otherBalls.append(otherb)

    for i in range(100): #初始化創建100個食物
        createFood(foodlist)

    ball = Ball(0, 0, INITIALWEIGHT, randomColor()) #建立第一個小球
    selfBalls.append(ball)

    fontObj = pygame.font.Font('C:/Windows/Fonts/msyh.ttc',20)  #字體對象,我用的是系統字體
    winfont = pygame.font.Font('C:/Windows/Fonts/msyh.ttc',36)

    allweight = 0
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                time.sleep(3)
                exit()
            if event.type == KEYUP: #響應鍵盤
                if event.key == K_w:
                    dirction = UP
                elif event.key == K_s:
                    dirction = DOWN
                elif event.key == K_d:
                    dirction = RIGHT
                elif event.key == K_a:
                    dirction = LEFT
                elif event.key == K_j:  #分裂
                    count = len(selfBalls)
                    if count < 16:
                        for i in range(count):
                            if selfBalls[i].weight > 20:
                                selfBalls[i].split(dirction)

        DISPLAYSURF.fill(LIGHTBLACK) #背景填充
        rec = pygame.Rect(-(camerax + HALFWHEIGHT), -(cameray + HALFWHEIGHT), WORLDWIDTH, WORLDHEIGHT) #邊界矩形
        drawBorder(rec)

        text = '重量爲:'+str(allweight)+'   敵人還剩:'+str(len(otherBalls))
        displayText(text,fontObj,WHITE,200,20)
        if len(foodlist)<500:  #當食物數量小於500時,增加食物
            createFood(foodlist)
        drawFoods(foodlist)

        if len(otherBalls)>0: #當其他球還有的時候進行喫球的操作
            balleatBall()
        if len(otherBalls)==0 or allweight>=10000:  #勝利條件
            displayText('恭喜你!最終你胖到了'+str(allweight)+'斤',winfont,RED,CENTERWIDTH,CENTERHEIGHT)
            pygame.display.update()
            time.sleep(3)
            pygame.quit()
        if len(selfBalls)==0:
            displayText('你被吃了~繼續努力吧~',winfont,RED,CENTERWIDTH,CENTERHEIGHT)
            pygame.display.update()
            time.sleep(3)
            pygame.quit()
        
        allweight = 0
        for b in selfBalls:  #得到所有重量和移動所有球
            allweight += b.weight
            b.move(dirction)

        for b in otherBalls: #移動其他的球
            b.move()
            b.eatFood()

        drawBalls(selfBalls)

        camerax = selfBalls[0].xpos - CENTERWIDTH
        cameray = selfBalls[0].ypos - CENTERHEIGHT

        for ball in selfBalls:
            ball.eatFood()

        drawBalls(otherBalls)

        if len(selfBalls)>=2:
            unite()
        pygame.display.update()
        FPSCLOCK.tick(FPS)

def displayText(text,fontObj,textcolor,xpos,ypos):  #顯示字函數
    textsurf = fontObj.render(text, True, textcolor)
    textRect = textsurf.get_rect()
    textRect.center = (xpos, ypos)
    DISPLAYSURF.blit(textsurf, textRect)

def balleatBall(): #我方球喫其他球,本遊戲不設置其他球互喫
    global selfBalls,otherBalls
    for ball1 in selfBalls:
        for ball2 in otherBalls:
            distance = math.sqrt((ball1.xpos - ball2.xpos) ** 2 + (ball1.ypos - ball2.ypos) ** 2)
            if distance < (ball1.radius + ball2.radius) / 2:
                if ball1.radius > ball2.radius + 3:
                    ball1.setWeight(ball1.weight + ball2.weight)
                    otherBalls.remove(ball2)
                elif ball1.radius+3 < ball2.radius :
                    ball2.setWeight(ball1.weight + ball2.weight)
                    selfBalls.remove(ball1)


def unite(): #聯合兩個小球
    global selfBalls
    for ball1 in selfBalls:
        for ball2 in selfBalls:
            if ball1!=ball2:
                distance = math.sqrt((ball1.xpos-ball2.xpos)**2+(ball1.ypos-ball2.ypos)**2)
                if distance<(ball1.radius+ball2.radius)/2:
                    ball1.setWeight(ball1.weight+ball2.weight)
                    selfBalls.remove(ball2)


def createFood(foodlist):
    xpos = random.randint(-HALFWWIDTH,HALFWWIDTH)
    ypos = random.randint(-HALFWHEIGHT,HALFWHEIGHT)
    weight = 5  #每個食物的重量
    radius = 3  #每個食物的半徑
    newfood = Food(xpos,ypos,weight,randomColor(),radius)
    foodlist.append(newfood)

def drawFoods(foodlist):
    global camerax,cameray
    for food in foodlist:
        pygame.draw.circle(DISPLAYSURF, food.color, ((food.xpos - camerax), (food.ypos - cameray)), food.radius)

def drawBalls(balls): #畫所有球
    global camerax,cameray
    for ball in balls:
        pos = (ball.xpos-camerax,ball.ypos-cameray)
        radius = ball.radius
        color = ball.color
        pygame.draw.circle(DISPLAYSURF,color,pos,radius)

def weightToSpeed(weight):#重量轉換爲速度
    if weight < 8000:
        return math.floor(-0.02*weight+200)
    elif weight >=8000:
        return 40  #最低速度爲40

def weightToRadius(weight):  #將小球的重量轉化爲半徑
    if weight < 100:
        return math.floor(0.1*weight + 10)
    elif weight>=100:
        return math.floor(2*math.sqrt(weight))

def drawBorder(rec): #畫邊界
    borderthick = 5
    pygame.draw.rect(DISPLAYSURF,WHITE,rec,borderthick)
    recleft = (rec[0]-CENTERWIDTH,rec[1]-CENTERHEIGHT,CENTERWIDTH,WORLDHEIGHT+HEIGHT)
    recright = (rec[0]+WORLDWIDTH,rec[1]-CENTERHEIGHT,CENTERWIDTH,WORLDHEIGHT+HEIGHT)
    rectop = (rec[0],rec[1]-CENTERHEIGHT,WORLDWIDTH,CENTERHEIGHT)
    recbottom = (rec[0],rec[1]+WORLDHEIGHT,WORLDWIDTH,CENTERHEIGHT)
    pygame.draw.rect(DISPLAYSURF,BLACK,recleft,0)
    pygame.draw.rect(DISPLAYSURF, BLACK, rectop, 0)
    pygame.draw.rect(DISPLAYSURF, BLACK, recright, 0)
    pygame.draw.rect(DISPLAYSURF, BLACK, recbottom, 0)

def drawBall(Obj):
    pygame.draw.circle(DISPLAYSURF,Obj.color,(Obj.xpos,Obj.ypos),Obj.radius)

def randomColor(): #隨機獲取顏色
    return (random.randint(1,255),random.randint(1,255),random.randint(1,255))


if __name__ =="__main__":
    main()

運行截圖:
在這裏插入圖片描述
在這裏插入圖片描述
代碼中比較困難的部分我進行了註解,整個程序的邏輯並不困難,就不贅敘了。

在以上代碼中,還有一些問題。

比如說我的窗口座標(camerax,cameray)是由selfBalls的第一個球的座標決定的,這就導致當小球多了的時候,有一些小球無法裝進窗口中。

當然還有其他的一些問題,但精力能力有限,僅能做到現在這番模樣。等以後能力提高再進行完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章