在玩很多游戏的时候,我们可以发现游戏里面的世界很大,但是整个窗口却最大不过我们屏幕大小,为了观察到整个世界,我们的视角窗口就会随着里面人物的移动不断的移动。
比如说游戏球球大作战,在玩这款游戏的时候我们会发现,我们的视角中心始终是我们的所有球,随着球的不断移动,我们的视角也不断的在整个球球世界里面移动。
对于这样的窗口视角的移动,我们可以选择增加两个变量,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的第一个球的坐标决定的,这就导致当小球多了的时候,有一些小球无法装进窗口中。
当然还有其他的一些问题,但精力能力有限,仅能做到现在这番模样。等以后能力提高再进行完善。