
'''
公眾號:一行資料,關注領取5T編程資料
'''
import pygame
import sys
import random
class Bird(object):
"""定義一個鳥類"""
def __init__(self):
"""定義初始化方法"""
self.birdRect = pygame.Rect(65, 50, 50, 50) # 鳥的矩形
# 定義鳥的3種狀態串列
self.birdStatus = [pygame.image.load("assets/1.png"),
pygame.image.load("assets/2.png"),
pygame.image.load("assets/dead.png")]
self.status = 0 # 默認飛行狀態
self.birdX = 120 # 鳥所在X軸坐標,即是向右飛行的速度
self.birdY = 350 # 鳥所在Y軸坐標,即上下飛行高度
self.jump = False # 默認情況小鳥自動降落
self.jumpSpeed = 10 # 跳躍高度
self.gravity = 5 # 重力
self.dead = False # 默認小鳥生命狀態為活著
def birdUpdate(self):
if self.jump:
# 小鳥跳躍
self.jumpSpeed -= 1 # 速度遞減,上升越來越慢
self.birdY -= self.jumpSpeed # 鳥Y軸坐標減小,小鳥上升
else:
# 小鳥墜落
self.gravity += 0.2 # 重力遞增,下降越來越快
self.birdY += self.gravity # 鳥Y軸坐標增加,小鳥下降
self.birdRect[1] = self.birdY # 更改Y軸位置
class Pipeline(object):
"""定義一個管道類"""
def __init__(self):
"""定義初始化方法"""
self.wallx = 400 # 管道所在X軸坐標
self.pineUp = pygame.image.load("assets/top.png")
self.pineDown = pygame.image.load("assets/bottom.png")
def updatePipeline(self):
""""管道移動方法"""
self.wallx -= 5 # 管道X軸坐標遞減,即管道向左移動
# 當管道運行到一定位置,即小鳥飛越管道,分數加1,并且重置管道
if self.wallx < -80:
global score
score += 1
self.wallx = 400
def createMap():
"""定義創建地圖的方法"""
screen.fill((255, 255, 255)) # 填充顏色
screen.blit(background, (0, 0)) # 填入到背景
# 顯示管道
screen.blit(Pipeline.pineUp, (Pipeline.wallx, -300)) # 上管道坐標位置
screen.blit(Pipeline.pineDown, (Pipeline.wallx, 500)) # 下管道坐標位置
Pipeline.updatePipeline() # 管道移動
# 顯示小鳥
if Bird.dead: # 撞管道狀態
Bird.status = 2
elif Bird.jump: # 起飛狀態
Bird.status = 1
screen.blit(Bird.birdStatus[Bird.status], (Bird.birdX, Bird.birdY)) # 設定小鳥的坐標
Bird.birdUpdate() # 鳥移動
# 顯示分數
screen.blit(font.render('Score:' + str(score), -1, (255, 255, 255)), (100, 50)) # 設定顏色及坐標位置
pygame.display.update() # 更新顯示
def checkDead():
# 上方管子的矩形位置
upRect = pygame.Rect(Pipeline.wallx, -300,
Pipeline.pineUp.get_width() - 10,
Pipeline.pineUp.get_height())
# 下方管子的矩形位置
downRect = pygame.Rect(Pipeline.wallx, 500,
Pipeline.pineDown.get_width() - 10,
Pipeline.pineDown.get_height())
# 檢測小鳥與上下方管子是否碰撞
if upRect.colliderect(Bird.birdRect) or downRect.colliderect(Bird.birdRect):
Bird.dead = True
# 檢測小鳥是否飛出上下邊界
if not 0 < Bird.birdRect[1] < height:
Bird.dead = True
return True
else:
return False
def getResutl():
final_text1 = "Game Over"
final_text2 = "Your final score is: " + str(score)
ft1_font = pygame.font.SysFont("Arial", 70) # 設定第一行文字字體
ft1_surf = font.render(final_text1, 1, (242, 3, 36)) # 設定第一行文字顏色
ft2_font = pygame.font.SysFont("Arial", 50) # 設定第二行文字字體
ft2_surf = font.render(final_text2, 1, (253, 177, 6)) # 設定第二行文字顏色
screen.blit(ft1_surf, [screen.get_width() / 2 - ft1_surf.get_width() / 2, 100]) # 設定第一行文字顯示位置
screen.blit(ft2_surf, [screen.get_width() / 2 - ft2_surf.get_width() / 2, 200]) # 設定第二行文字顯示位置
pygame.display.flip() # 更新整個待顯示的Surface物件到螢屏上
if __name__ == '__main__':
"""主程式"""
pygame.init() # 初始化pygame
pygame.font.init() # 初始化字體
font = pygame.font.SysFont("ziti.ttf", 50) # 設定字體和大小
size = width, height = 400, 650 # 設定視窗
screen = pygame.display.set_mode(size) # 顯示視窗
clock = pygame.time.Clock() # 設定時鐘
Pipeline = Pipeline() # 實體化管道類
Bird = Bird() # 實體化鳥類
score = 0
while True:
clock.tick(60) # 每秒執行60次
# 輪詢事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and not Bird.dead:
Bird.jump = True # 跳躍
Bird.gravity = 5 # 重力
Bird.jumpSpeed = 10 # 跳躍速度
background = pygame.image.load("assets/background.png") # 加載背景圖片
if checkDead(): # 檢測小鳥生命狀態
getResutl() # 如果小鳥死亡,顯示游戲總分數
else:
createMap() # 創建地圖
pygame.quit()
俄羅斯方塊
# -*- coding:utf-8 -*-
'''
公眾號:【一行資料】,關注領取5T編程資料
'''
import sys
import random, copy
import pygame as pg
from pygame.locals import *
'''
常量宣告
'''
EMPTY_CELL = 0 # 空區標識,表示沒有方塊
FALLING_BLOCK = 1 # 下落中的方塊標識,也就是活動方塊,
STATIC_BLOCK = 2 # 固實方塊標識
'''
全域變數宣告
變數值以sysInit函式中初始化后的結果為準
'''
defaultFont = None # 默認字體
screen = None # 螢屏輸出物件
backSurface = None # 影像輸出緩沖畫板
score = 0 # 玩家得分記錄
clearLineScore = 0 # 玩家清除的方塊行數
level = 1 # 關卡等級
clock = None # 游戲時鐘
nowBlock = None # 當前下落中的方塊
nextBlock = None # 下一個將出現的方塊
fallSpeed = 10 # 當前方塊下落速度
beginFallSpeed = fallSpeed # 游戲初始時方塊下落速度
speedBuff = 0 # 下落速度緩沖變數
keyBuff = None # 上一次按鍵記錄
maxBlockWidth = 10 # 舞臺堆疊區X軸最大可容納基礎方塊數
maxBlockHeight = 18 # 舞臺堆疊區Y軸最大可容納基礎方塊數
blockWidth = 30 # 以像素為單位的基礎方塊寬度
blockHeight = 30 # 以像素為單位的基礎方塊高度
blocks = [] # 方塊形狀矩陣四維串列,第一維為不同的方塊形狀,第二維為每個方塊形狀不同的方向(以0下標起始,一共四個方向),第三維為Y軸方塊形狀占用情況,第四維為X軸方塊形狀占用情況,矩陣中0表示沒有方塊,1表示有方塊,
stage = [] # 舞臺堆疊區矩陣二維串列,第一維為Y軸方塊占用情況,第二維為X軸方塊占用情況,矩陣中0表示沒有方塊,1表示有固實方塊,2表示有活動方塊,
gameOver = False # 游戲結束標志
pause = False # 游戲暫圖示志
def printTxt(content, x, y, font, screen, color=(255, 255, 255)):
'''顯示文本
args:
content:待顯示文本內容
x,y:顯示坐標
font:字體
screen:輸出的screen
color:顏色
'''
imgTxt = font.render(content, True, color)
screen.blit(imgTxt, (x, y))
class point(object):
'''平面坐標點類
attributes:
x,y:坐標值
'''
def __init__(self, x, y):
self.__x = x
self.__y = y
def getx(self):
return self.__x
def setx(self, x):
self.__x = x
x = property(getx, setx)
def gety(self):
return self.__y
def sety(self, y):
self.__y = y
y = property(gety, sety)
def __str__(self):
return "{x:" + "{:.0f}".format(self.__x) + ",y:" + "{:.0f}".format(self.__y) + "}"
class blockSprite(object):
'''
方塊形狀精靈類
下落方塊的定義全靠它了,
attributes:
shape:方塊形狀編號
direction:方塊方向編號
xy,方塊形狀左上角方塊坐標
block:方塊形狀矩陣
'''
def __init__(self, shape, direction, xy):
self.shape = shape
self.direction = direction
self.xy = xy
def chgDirection(self, direction):
'''
改變方塊的方向
args:
direction:1為向右轉,0為向左轉,
'''
dirNumb = len(blocks[self.shape]) - 1
if direction == 1:
self.direction += 1
if self.direction > dirNumb:
self.direction = 0
else:
self.direction -= 1
if self.direction < 0:
self.direction = dirNumb
def clone(self):
'''
克隆本體
return:
回傳自身的克隆
'''
return blockSprite(self.shape, self.direction, point(self.xy.x, self.xy.y))
def _getBlock(self):
return blocks[self.shape][self.direction]
block = property(_getBlock)
def getConf(fileName):
'''
從組態檔中讀取方塊形狀資料
每個方塊以4*4矩陣表示形狀,組態檔每行代表一個方塊,用分號分隔矩陣行,用逗號分隔矩陣列,0表示沒有方塊,1表示有方塊,
因為此程式只針對俄羅斯方塊的經典版,所以方塊矩陣大小以硬編碼的形式寫死為4*4,
args:
fileName:組態檔名
'''
global blocks # blocks記錄方塊形狀,
with open(fileName, 'rt') as fp:
for temp in fp.readlines():
blocks.append([])
blocksNumb = len(blocks) - 1
blocks[blocksNumb] = []
# 每種方塊形狀有四個方向,以0~3表示,組態檔中只記錄一個方向形狀,另外三個方向的矩陣排列在sysInit中通過呼叫transform計算出來,
blocks[blocksNumb].append([])
row = temp.split(";")
for r in range(len(row)):
col = []
ct = row[r].split(",")
# 對矩陣列資料做規整,首先將非“1”的值全修正成“0”以過濾空字串或回車符,
for c in range(len(ct)):
if ct[c] != "1":
col.append(0)
else:
col.append(1)
# 將不足4列的矩陣通過補“0”的方式,補足4列,
for c in range(len(ct) - 1, 3):
col.append(0)
blocks[blocksNumb][0].append(col)
# 如果矩陣某行沒有方塊,則組態檔中可以省略此行,程式會在末尾補上空行資料,
for r in range(len(row) - 1, 3):
blocks[blocksNumb][0].append([0, 0, 0, 0])
blocks[blocksNumb][0] = formatBlock(blocks[blocksNumb][0])
def sysInit():
'''
系統初始化
包括pygame環境初始化,全域變數賦值,生成每個方塊形狀的四個方向矩陣,
'''
global defaultFont, screen, backSurface, clock, blocks, stage, gameOver, fallSpeed, beginFallSpeed, nowBlock, nextBlock, score, level, clearLineScore, pause
# pygame運行環境初始化
pg.init()
screen = pg.display.set_mode((500, 550))
backSurface = pg.Surface((screen.get_rect().width, screen.get_rect().height))
pg.display.set_caption("block")
clock = pg.time.Clock()
pg.mouse.set_visible(False)
# 游戲全域變數初始化
defaultFont = pg.font.Font(None, 16) # yh.ttf這個字體檔案請自行上網搜索下載,如果找不到就隨便用個ttf格式字體檔案替換一下,
nowBlock = None
nextBlock = None
gameOver = False
pause = False
score = 0
level = 1
clearLineScore = 0
beginFallSpeed = 20
fallSpeed = beginFallSpeed - level * 2
# 初始化游戲舞臺
stage = []
for y in range(maxBlockHeight):
stage.append([])
for x in range(maxBlockWidth):
stage[y].append(EMPTY_CELL)
# 生成每個方塊形狀4個方向的矩陣資料
for x in range(len(blocks)):
# 因為重新開始游戲時會呼叫sysinit對系統所有引數重新初始化,為了避免方向矩陣資料重新生成,需要在此判斷是否已經生成,如果已經生成則跳過,
if len(blocks[x]) < 2:
t = blocks[x][0]
for i in range(3):
t = transform(t, 1)
blocks[x].append(formatBlock(t))
# transform,removeTopBlank,formatBlock這三個函式只為生成方塊形狀4個方向矩陣使用,在游戲其他環節無作用,在閱讀程式時可以先跳過,
def transform(block, direction=0):
'''
生成指定方塊形狀轉換方向后的矩陣資料
args:
block:方塊形狀矩陣引數
direction:轉換的方向,0代表向左,1代表向右
return:
變換方向后的方塊形狀矩陣引數
'''
result = []
for y in range(4):
result.append([])
for x in range(4):
if direction == 0:
result[y].append(block[x][3 - y])
else:
result[y].append(block[3 - x][y])
return result
def removeTopBlank(block):
'''
清除方塊矩陣頂部空行資料
args:
block:方塊開關矩陣
return:
整理后的方塊矩陣資料
'''
result = copy.deepcopy(block)
blankNumb = 0
while sum(result[0]) < 1 and blankNumb < 4:
del result[0]
result.append([0, 0, 0, 0])
blankNumb += 1
return result
def formatBlock(block):
'''
整理方塊矩陣資料,使方塊在矩陣中處于左上角的位置
args:
block:方塊開關矩陣
return:
整理后的方塊矩陣資料
'''
result = removeTopBlank(block)
# 將矩陣右轉,用于計算左側X軸線空行,計算完成后再轉回
result = transform(result, 1)
result = removeTopBlank(result)
result = transform(result, 0)
return result
def checkDeany(sprite):
'''
檢查下落方塊是否與舞臺堆疊區中固實方塊發生碰撞
args:
sprite:下落方塊
return:
如果發生碰撞則回傳True
'''
topX = sprite.xy.x
topY = sprite.xy.y
for y in range(len(sprite.block)):
for x in range(len(sprite.block[y])):
if sprite.block[y][x] == 1:
yInStage = topY + y
xInStage = topX + x
if yInStage > maxBlockHeight - 1 or yInStage < 0:
return True
if xInStage > maxBlockWidth - 1 or xInStage < 0:
return True
if stage[yInStage][xInStage] == STATIC_BLOCK:
return True
return False
def checkLine():
'''
檢測堆疊區是否有可消除的整行固實方塊
根據檢測結果重新生成堆疊區矩陣資料,呼叫updateScore函式更新玩家積分等資料,
return:
本輪下落周期消除的固實方塊行數
'''
global stage
clearCount = 0 # 本輪下落周期消除的固實方塊行數
tmpStage = [] # 根據消除情況新生成的堆疊區矩陣,在有更新的情況下會替換全域的堆疊區矩陣,
for y in stage:
# 因為固實方塊在堆疊矩陣里以2表示,所以判斷方塊是否已經滿一整行只要計算矩陣行數值合計是否等于堆疊區X軸最大方塊數*2就可以,
if sum(y) >= maxBlockWidth * 2:
tmpStage.insert(0, maxBlockWidth * [0])
clearCount += 1
else:
tmpStage.append(y)
if clearCount > 0:
stage = tmpStage
updateScore(clearCount)
return clearCount
def updateStage(sprite, updateType=1):
'''
將下落方塊坐標資料更新到堆疊區資料中,下落方塊涉及的坐標在堆疊區中用數字1標識,固實方塊在堆疊區中用數字2標識,
args:
sprite:下落方塊形狀
updateType:更新方式,0代表清除,1代表動態加入,2代表固實加入,
'''
global stage
topX = sprite.xy.x
topY = sprite.xy.y
for y in range(len(sprite.block)):
for x in range(len(sprite.block[y])):
if sprite.block[y][x] == 1:
if updateType == 0:
if stage[topY + y][topX + x] == FALLING_BLOCK:
stage[topY + y][topX + x] = EMPTY_CELL
elif updateType == 1:
if stage[topY + y][topX + x] == EMPTY_CELL:
stage[topY + y][topX + x] = FALLING_BLOCK
else:
stage[topY + y][topX + x] = STATIC_BLOCK
def updateScore(clearCount):
'''
更新玩家游戲記錄,包括積分、關卡、消除方塊行數,并且根據關卡數更新方塊下落速度,
args:
clearCount:本輪下落周期內清除的方塊行數,
return:
當前游戲的最新積分
'''
global score, fallSpeed, level, clearLineScore
prizePoint = 0 # 額外獎勵分數,同時消除的行數越多,獎勵分值越高,
if clearCount > 1:
if clearCount < 4:
prizePoint = clearCount ** clearCount
else:
prizePoint = clearCount * 5
score += (clearCount + prizePoint) * level + 10
# 玩得再牛又有何用? :)
if score > 99999999:
score = 0
clearLineScore += clearCount
if clearLineScore > 10:
clearLineScore = 0
level += 1
if level > (beginFallSpeed / 2):
level = 1
fallSpeed = beginFallSpeed
fallSpeed = beginFallSpeed - level * 2
return score
def drawStage(drawScreen):
'''
在給定的畫布上繪制舞臺
args:
drawScreen:待繪制的畫布
'''
staticColor = 30, 102, 76 # 固實方塊顏色
activeColor = 255, 239, 0 # 方塊形狀顏色
fontColor = 200, 10, 120 # 文字顏色
baseRect = 0, 0, blockWidth * maxBlockWidth + 1, blockHeight * maxBlockHeight + 1 # 堆疊區方框
# 繪制堆疊區外框
drawScreen.fill((180, 200, 170))
pg.draw.rect(drawScreen, staticColor, baseRect, 1)
# 繪制堆疊區內的所有方塊,包括下落方塊形狀
for y in range(len(stage)):
for x in range(len(stage[y])):
baseRect = x * blockWidth, y * blockHeight, blockWidth, blockHeight
if stage[y][x] == 2:
pg.draw.rect(drawScreen, staticColor, baseRect)
elif stage[y][x] == 1:
pg.draw.rect(drawScreen, activeColor, baseRect)
# 繪制下一個登場的下落方塊形狀
printTxt("Next:", 320, 350, defaultFont, backSurface, fontColor)
if nextBlock != None:
for y in range(len(nextBlock.block)):
for x in range(len(nextBlock.block[y])):
baseRect = 320 + x * blockWidth, 380 + y * blockHeight, blockWidth, blockHeight
if nextBlock.block[y][x] == 1:
pg.draw.rect(drawScreen, activeColor, baseRect)
# 繪制關卡、積分、當前關卡消除整行數
printTxt("Level:%d" % level, 320, 40, defaultFont, backSurface, fontColor)
printTxt("Score:%d" % score, 320, 70, defaultFont, backSurface, fontColor)
printTxt("Clear:%d" % clearLineScore, 320, 100, defaultFont, backSurface, fontColor)
# 特殊游戲狀態的輸出
if gameOver:
printTxt("GAME OVER", 230, 200, defaultFont, backSurface, fontColor)
printTxt("<PRESS ENTER TO REPLAY>", 200, 260, defaultFont, backSurface, fontColor)
if pause:
printTxt("Game pausing", 230, 200, defaultFont, backSurface, fontColor)
printTxt("<PRESS ENTER TO CONTINUE>", 200, 260, defaultFont, backSurface, fontColor)
def process():
'''
游戲控制及邏輯處理
'''
global gameOver, nowBlock, nextBlock, speedBuff, backSurface, keyBuff, pause
if nextBlock is None:
nextBlock = blockSprite(random.randint(0, len(blocks) - 1), random.randint(0, 3),
point(maxBlockWidth + 4, maxBlockHeight))
if nowBlock is None:
nowBlock = nextBlock.clone()
nowBlock.xy = point(maxBlockWidth // 2, 0)
nextBlock = blockSprite(random.randint(0, len(blocks) - 1), random.randint(0, 3),
point(maxBlockWidth + 4, maxBlockHeight))
# 每次生成新的下落方塊形狀時檢測碰撞,如果新的方塊形狀一出現就發生碰撞,則顯然玩家已經沒有機會了,
gameOver = checkDeany(nowBlock)
# 游戲失敗后,要將活動方塊形狀做固實處理
if gameOver:
updateStage(nowBlock, 2)
'''
對于下落方塊形狀操控以及移動,采用影子形狀進行預判斷,如果沒有碰撞則將變化應用到下落方塊形狀上,否則不變化,
'''
tmpBlock = nowBlock.clone() # 影子方塊形狀
'''
處理用戶輸入
對于用戶輸入分為兩部分處理,
第一部分,將退出、暫停、重新開始以及形狀變換的操作以敲擊事件處理,
這樣做的好處是只對敲擊一次鍵盤做出處理,避免用戶按住單一按鍵后程式反復處理影響操控,特別是形狀變換操作,敲擊一次鍵盤換變一次方向,玩家很容易控制,
'''
for event in pg.event.get():
if event.type == pg.QUIT:
sys.exit()
pg.quit()
elif event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
sys.exit()
pg.quit()
elif event.key == pg.K_RETURN:
if gameOver:
sysInit()
return
elif pause:
pause = False
else:
pause = True
return
elif not gameOver and not pause:
if event.key == pg.K_SPACE:
tmpBlock.chgDirection(1)
elif event.key == pg.K_UP:
tmpBlock.chgDirection(0)
if not gameOver and not pause:
'''
用戶輸入處理第二部分,將左右移動和快速下落的操作以按下事件處理,
這樣做的好處是不需要玩家反復敲擊鍵盤進行操作,保證了操作的連貫性,
由于連續移動的速度太快,不利于定位,所以在程式中采用了簡單的輸入級訓處理,即通過keyBuff保存上一次操作按鍵,如果此次按鍵與上一次按鍵相同,則跳過此輪按鍵處理,
'''
keys = pg.key.get_pressed()
if keys[K_DOWN]:
tmpBlock.xy = point(tmpBlock.xy.x, tmpBlock.xy.y + 1)
keyBuff = None
elif keys[K_LEFT]:
if keyBuff != pg.K_LEFT:
tmpBlock.xy = point(tmpBlock.xy.x - 1, tmpBlock.xy.y)
keyBuff = pg.K_LEFT
else:
keyBuff = None
elif keys[K_RIGHT]:
if keyBuff != pg.K_RIGHT:
tmpBlock.xy = point(tmpBlock.xy.x + 1, tmpBlock.xy.y)
keyBuff = pg.K_RIGHT
else:
keyBuff = None
if not checkDeany(tmpBlock):
updateStage(nowBlock, 0)
nowBlock = tmpBlock.clone()
# 處理自動下落
speedBuff += 1
if speedBuff >= fallSpeed:
speedBuff = 0
tmpBlock = nowBlock.clone()
tmpBlock.xy = point(nowBlock.xy.x, nowBlock.xy.y + 1)
if not checkDeany(tmpBlock):
updateStage(nowBlock, 0)
nowBlock = tmpBlock.clone()
updateStage(nowBlock, 1)
else:
# 在自動下落程序中一但發生活動方塊形狀的碰撞,則將活動方塊形狀做固實處理,并檢測是否有可消除的整行方塊
updateStage(nowBlock, 2)
checkLine()
nowBlock = None
else:
updateStage(nowBlock, 1)
drawStage(backSurface)
screen.blit(backSurface, (0, 0))
pg.display.update()
clock.tick(40)
def main():
'''
主程式
'''
getConf("elsfk.cfg")
sysInit()
while True:
process()
if __name__ == "__main__":
main()
我的世界

'''
公眾號:【一行資料】,關注領取5T編程資料
'''
from __future__ import division
import sys
import math
import random
import time
from collections import deque
from pyglet import image
from pyglet.gl import *
from pyglet.graphics import TextureGroup
from pyglet.window import key, mouse
TICKS_PER_SEC = 60
# Size of sectors used to ease block loading.
SECTOR_SIZE = 16
WALKING_SPEED = 5
FLYING_SPEED = 15
GRAVITY = 20.0
MAX_JUMP_HEIGHT = 1.0 # About the height of a block.
# To derive the formula for calculating jump speed, first solve
# v_t = v_0 + a * t
# for the time at which you achieve maximum height, where a is the acceleration
# due to gravity and v_t = 0. This gives:
# t = - v_0 / a
# Use t and the desired MAX_JUMP_HEIGHT to solve for v_0 (jump speed) in
# s = s_0 + v_0 * t + (a * t^2) / 2
JUMP_SPEED = math.sqrt(2 * GRAVITY * MAX_JUMP_HEIGHT)
TERMINAL_VELOCITY = 50
PLAYER_HEIGHT = 2
if sys.version_info[0] >= 3:
xrange = range
def cube_vertices(x, y, z, n):
""" Return the vertices of the cube at position x, y, z with size 2*n.
"""
return [
x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top
x-n,y-n,z-n, x+n,y-n,z-n, x+n,y-n,z+n, x-n,y-n,z+n, # bottom
x-n,y-n,z-n, x-n,y-n,z+n, x-n,y+n,z+n, x-n,y+n,z-n, # left
x+n,y-n,z+n, x+n,y-n,z-n, x+n,y+n,z-n, x+n,y+n,z+n, # right
x-n,y-n,z+n, x+n,y-n,z+n, x+n,y+n,z+n, x-n,y+n,z+n, # front
x+n,y-n,z-n, x-n,y-n,z-n, x-n,y+n,z-n, x+n,y+n,z-n, # back
]
def tex_coord(x, y, n=4):
""" Return the bounding vertices of the texture square.
"""
m = 1.0 / n
dx = x * m
dy = y * m
return dx, dy, dx + m, dy, dx + m, dy + m, dx, dy + m
def tex_coords(top, bottom, side):
""" Return a list of the texture squares for the top, bottom and side.
"""
top = tex_coord(*top)
bottom = tex_coord(*bottom)
side = tex_coord(*side)
result = []
result.extend(top)
result.extend(bottom)
result.extend(side * 4)
return result
TEXTURE_PATH = 'texture.png'
GRASS = tex_coords((1, 0), (0, 1), (0, 0))
SAND = tex_coords((1, 1), (1, 1), (1, 1))
BRICK = tex_coords((2, 0), (2, 0), (2, 0))
STONE = tex_coords((2, 1), (2, 1), (2, 1))
FACES = [
( 0, 1, 0),
( 0,-1, 0),
(-1, 0, 0),
( 1, 0, 0),
( 0, 0, 1),
( 0, 0,-1),
]
def normalize(position):
""" Accepts `position` of arbitrary precision and returns the block
containing that position.
Parameters
----------
position : tuple of len 3
Returns
-------
block_position : tuple of ints of len 3
"""
x, y, z = position
x, y, z = (int(round(x)), int(round(y)), int(round(z)))
return (x, y, z)
def sectorize(position):
""" Returns a tuple representing the sector for the given `position`.
Parameters
----------
position : tuple of len 3
Returns
-------
sector : tuple of len 3
"""
x, y, z = normalize(position)
x, y, z = x // SECTOR_SIZE, y // SECTOR_SIZE, z // SECTOR_SIZE
return (x, 0, z)
class Model(object):
def __init__(self):
# A Batch is a collection of vertex lists for batched rendering.
self.batch = pyglet.graphics.Batch()
# A TextureGroup manages an OpenGL texture.
self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture())
# A mapping from position to the texture of the block at that position.
# This defines all the blocks that are currently in the world.
self.world = {}
# Same mapping as `world` but only contains blocks that are shown.
self.shown = {}
# Mapping from position to a pyglet `VertextList` for all shown blocks.
self._shown = {}
# Mapping from sector to a list of positions inside that sector.
self.sectors = {}
# Simple function queue implementation. The queue is populated with
# _show_block() and _hide_block() calls
self.queue = deque()
self._initialize()
def _initialize(self):
""" Initialize the world by placing all the blocks.
"""
n = 80 # 1/2 width and height of world
s = 1 # step size
y = 0 # initial y height
for x in xrange(-n, n + 1, s):
for z in xrange(-n, n + 1, s):
# create a layer stone an grass everywhere.
self.add_block((x, y - 2, z), GRASS, immediate=False)
self.add_block((x, y - 3, z), STONE, immediate=False)
if x in (-n, n) or z in (-n, n):
# create outer walls.
for dy in xrange(-2, 3):
self.add_block((x, y + dy, z), STONE, immediate=False)
# generate the hills randomly
o = n - 10
for _ in xrange(120):
a = random.randint(-o, o) # x position of the hill
b = random.randint(-o, o) # z position of the hill
c = -1 # base of the hill
h = random.randint(1, 6) # height of the hill
s = random.randint(4, 8) # 2 * s is the side length of the hill
d = 1 # how quickly to taper off the hills
t = random.choice([GRASS, SAND, BRICK])
for y in xrange(c, c + h):
for x in xrange(a - s, a + s + 1):
for z in xrange(b - s, b + s + 1):
if (x - a) ** 2 + (z - b) ** 2 > (s + 1) ** 2:
continue
if (x - 0) ** 2 + (z - 0) ** 2 < 5 ** 2:
continue
self.add_block((x, y, z), t, immediate=False)
s -= d # decrement side lenth so hills taper off
def hit_test(self, position, vector, max_distance=8):
""" Line of sight search from current position. If a block is
intersected it is returned, along with the block previously in the line
of sight. If no block is found, return None, None.
Parameters
----------
position : tuple of len 3
The (x, y, z) position to check visibility from.
vector : tuple of len 3
The line of sight vector.
max_distance : int
How many blocks away to search for a hit.
"""
m = 8
x, y, z = position
dx, dy, dz = vector
previous = None
for _ in xrange(max_distance * m):
key = normalize((x, y, z))
if key != previous and key in self.world:
return key, previous
previous = key
x, y, z = x + dx / m, y + dy / m, z + dz / m
return None, None
def exposed(self, position):
""" Returns False is given `position` is surrounded on all 6 sides by
blocks, True otherwise.
"""
x, y, z = position
for dx, dy, dz in FACES:
if (x + dx, y + dy, z + dz) not in self.world:
return True
return False
def add_block(self, position, texture, immediate=True):
""" Add a block with the given `texture` and `position` to the world.
Parameters
----------
position : tuple of len 3
The (x, y, z) position of the block to add.
texture : list of len 3
The coordinates of the texture squares. Use `tex_coords()` to
generate.
immediate : bool
Whether or not to draw the block immediately.
"""
if position in self.world:
self.remove_block(position, immediate)
self.world[position] = texture
self.sectors.setdefault(sectorize(position), []).append(position)
if immediate:
if self.exposed(position):
self.show_block(position)
self.check_neighbors(position)
def remove_block(self, position, immediate=True):
""" Remove the block at the given `position`.
Parameters
----------
position : tuple of len 3
The (x, y, z) position of the block to remove.
immediate : bool
Whether or not to immediately remove block from canvas.
"""
del self.world[position]
self.sectors[sectorize(position)].remove(position)
if immediate:
if position in self.shown:
self.hide_block(position)
self.check_neighbors(position)
def check_neighbors(self, position):
""" Check all blocks surrounding `position` and ensure their visual
state is current. This means hiding blocks that are not exposed and
ensuring that all exposed blocks are shown. Usually used after a block
is added or removed.
"""
x, y, z = position
for dx, dy, dz in FACES:
key = (x + dx, y + dy, z + dz)
if key not in self.world:
continue
if self.exposed(key):
if key not in self.shown:
self.show_block(key)
else:
if key in self.shown:
self.hide_block(key)
def show_block(self, position, immediate=True):
""" Show the block at the given `position`. This method assumes the
block has already been added with add_block()
Parameters
----------
position : tuple of len 3
The (x, y, z) position of the block to show.
immediate : bool
Whether or not to show the block immediately.
"""
texture = self.world[position]
self.shown[position] = texture
if immediate:
self._show_block(position, texture)
else:
self._enqueue(self._show_block, position, texture)
def _show_block(self, position, texture):
""" Private implementation of the `show_block()` method.
Parameters
----------
position : tuple of len 3
The (x, y, z) position of the block to show.
texture : list of len 3
The coordinates of the texture squares. Use `tex_coords()` to
generate.
"""
x, y, z = position
vertex_data = cube_vertices(x, y, z, 0.5)
texture_data = list(texture)
# create vertex list
# FIXME Maybe `add_indexed()` should be used instead
self._shown[position] = self.batch.add(24, GL_QUADS, self.group,
('v3f/static', vertex_data),
('t2f/static', texture_data))
def hide_block(self, position, immediate=True):
""" Hide the block at the given `position`. Hiding does not remove the
block from the world.
Parameters
----------
position : tuple of len 3
The (x, y, z) position of the block to hide.
immediate : bool
Whether or not to immediately remove the block from the canvas.
"""
self.shown.pop(position)
if immediate:
self._hide_block(position)
else:
self._enqueue(self._hide_block, position)
def _hide_block(self, position):
""" Private implementation of the 'hide_block()` method.
"""
self._shown.pop(position).delete()
def show_sector(self, sector):
""" Ensure all blocks in the given sector that should be shown are
drawn to the canvas.
"""
for position in self.sectors.get(sector, []):
if position not in self.shown and self.exposed(position):
self.show_block(position, False)
def hide_sector(self, sector):
""" Ensure all blocks in the given sector that should be hidden are
removed from the canvas.
"""
for position in self.sectors.get(sector, []):
if position in self.shown:
self.hide_block(position, False)
def change_sectors(self, before, after):
""" Move from sector `before` to sector `after`. A sector is a
contiguous x, y sub-region of world. Sectors are used to speed up
world rendering.
"""
before_set = set()
after_set = set()
pad = 4
for dx in xrange(-pad, pad + 1):
for dy in [0]: # xrange(-pad, pad + 1):
for dz in xrange(-pad, pad + 1):
if dx ** 2 + dy ** 2 + dz ** 2 > (pad + 1) ** 2:
continue
if before:
x, y, z = before
before_set.add((x + dx, y + dy, z + dz))
if after:
x, y, z = after
after_set.add((x + dx, y + dy, z + dz))
show = after_set - before_set
hide = before_set - after_set
for sector in show:
self.show_sector(sector)
for sector in hide:
self.hide_sector(sector)
def _enqueue(self, func, *args):
""" Add `func` to the internal queue.
"""
self.queue.append((func, args))
def _dequeue(self):
""" Pop the top function from the internal queue and call it.
"""
func, args = self.queue.popleft()
func(*args)
def process_queue(self):
""" Process the entire queue while taking periodic breaks. This allows
the game loop to run smoothly. The queue contains calls to
_show_block() and _hide_block() so this method should be called if
add_block() or remove_block() was called with immediate=False
"""
start = time.clock()
while self.queue and time.clock() - start < 1.0 / TICKS_PER_SEC:
self._dequeue()
def process_entire_queue(self):
""" Process the entire queue with no breaks.
"""
while self.queue:
self._dequeue()
class Window(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
# Whether or not the window exclusively captures the mouse.
self.exclusive = False
# When flying gravity has no effect and speed is increased.
self.flying = False
# Strafing is moving lateral to the direction you are facing,
# e.g. moving to the left or right while continuing to face forward.
#
# First element is -1 when moving forward, 1 when moving back, and 0
# otherwise. The second element is -1 when moving left, 1 when moving
# right, and 0 otherwise.
self.strafe = [0, 0]
# Current (x, y, z) position in the world, specified with floats. Note
# that, perhaps unlike in math class, the y-axis is the vertical axis.
self.position = (0, 0, 0)
# First element is rotation of the player in the x-z plane (ground
# plane) measured from the z-axis down. The second is the rotation
# angle from the ground plane up. Rotation is in degrees.
#
# The vertical plane rotation ranges from -90 (looking straight down) to
# 90 (looking straight up). The horizontal rotation range is unbounded.
self.rotation = (0, 0)
# Which sector the player is currently in.
self.sector = None
# The crosshairs at the center of the screen.
self.reticle = None
# Velocity in the y (upward) direction.
self.dy = 0
# A list of blocks the player can place. Hit num keys to cycle.
self.inventory = [BRICK, GRASS, SAND]
# The current block the user can place. Hit num keys to cycle.
self.block = self.inventory[0]
# Convenience list of num keys.
self.num_keys = [
key._1, key._2, key._3, key._4, key._5,
key._6, key._7, key._8, key._9, key._0]
# Instance of the model that handles the world.
self.model = Model()
# The label that is displayed in the top left of the canvas.
self.label = pyglet.text.Label('', font_name='Arial', font_size=18,
x=10, y=self.height - 10, anchor_x='left', anchor_y='top',
color=(0, 0, 0, 255))
# This call schedules the `update()` method to be called
# TICKS_PER_SEC. This is the main game event loop.
pyglet.clock.schedule_interval(self.update, 1.0 / TICKS_PER_SEC)
def set_exclusive_mouse(self, exclusive):
""" If `exclusive` is True, the game will capture the mouse, if False
the game will ignore the mouse.
"""
super(Window, self).set_exclusive_mouse(exclusive)
self.exclusive = exclusive
def get_sight_vector(self):
""" Returns the current line of sight vector indicating the direction
the player is looking.
"""
x, y = self.rotation
# y ranges from -90 to 90, or -pi/2 to pi/2, so m ranges from 0 to 1 and
# is 1 when looking ahead parallel to the ground and 0 when looking
# straight up or down.
m = math.cos(math.radians(y))
# dy ranges from -1 to 1 and is -1 when looking straight down and 1 when
# looking straight up.
dy = math.sin(math.radians(y))
dx = math.cos(math.radians(x - 90)) * m
dz = math.sin(math.radians(x - 90)) * m
return (dx, dy, dz)
def get_motion_vector(self):
""" Returns the current motion vector indicating the velocity of the
player.
Returns
-------
vector : tuple of len 3
Tuple containing the velocity in x, y, and z respectively.
"""
if any(self.strafe):
x, y = self.rotation
strafe = math.degrees(math.atan2(*self.strafe))
y_angle = math.radians(y)
x_angle = math.radians(x + strafe)
if self.flying:
m = math.cos(y_angle)
dy = math.sin(y_angle)
if self.strafe[1]:
# Moving left or right.
dy = 0.0
m = 1
if self.strafe[0] > 0:
# Moving backwards.
dy *= -1
# When you are flying up or down, you have less left and right
# motion.
dx = math.cos(x_angle) * m
dz = math.sin(x_angle) * m
else:
dy = 0.0
dx = math.cos(x_angle)
dz = math.sin(x_angle)
else:
dy = 0.0
dx = 0.0
dz = 0.0
return (dx, dy, dz)
def update(self, dt):
""" This method is scheduled to be called repeatedly by the pyglet
clock.
Parameters
----------
dt : float
The change in time since the last call.
"""
self.model.process_queue()
sector = sectorize(self.position)
if sector != self.sector:
self.model.change_sectors(self.sector, sector)
if self.sector is None:
self.model.process_entire_queue()
self.sector = sector
m = 8
dt = min(dt, 0.2)
for _ in xrange(m):
self._update(dt / m)
def _update(self, dt):
""" Private implementation of the `update()` method. This is where most
of the motion logic lives, along with gravity and collision detection.
Parameters
----------
dt : float
The change in time since the last call.
"""
# walking
speed = FLYING_SPEED if self.flying else WALKING_SPEED
d = dt * speed # distance covered this tick.
dx, dy, dz = self.get_motion_vector()
# New position in space, before accounting for gravity.
dx, dy, dz = dx * d, dy * d, dz * d
# gravity
if not self.flying:
# Update your vertical speed: if you are falling, speed up until you
# hit terminal velocity; if you are jumping, slow down until you
# start falling.
self.dy -= dt * GRAVITY
self.dy = max(self.dy, -TERMINAL_VELOCITY)
dy += self.dy * dt
# collisions
x, y, z = self.position
x, y, z = self.collide((x + dx, y + dy, z + dz), PLAYER_HEIGHT)
self.position = (x, y, z)
def collide(self, position, height):
""" Checks to see if the player at the given `position` and `height`
is colliding with any blocks in the world.
Parameters
----------
position : tuple of len 3
The (x, y, z) position to check for collisions at.
height : int or float
The height of the player.
Returns
-------
position : tuple of len 3
The new position of the player taking into account collisions.
"""
# How much overlap with a dimension of a surrounding block you need to
# have to count as a collision. If 0, touching terrain at all counts as
# a collision. If .49, you sink into the ground, as if walking through
# tall grass. If >= .5, you'll fall through the ground.
pad = 0.25
p = list(position)
np = normalize(position)
for face in FACES: # check all surrounding blocks
for i in xrange(3): # check each dimension independently
if not face[i]:
continue
# How much overlap you have with this dimension.
d = (p[i] - np[i]) * face[i]
if d < pad:
continue
for dy in xrange(height): # check each height
op = list(np)
op[1] -= dy
op[i] += face[i]
if tuple(op) not in self.model.world:
continue
p[i] -= (d - pad) * face[i]
if face == (0, -1, 0) or face == (0, 1, 0):
# You are colliding with the ground or ceiling, so stop
# falling / rising.
self.dy = 0
break
return tuple(p)
def on_mouse_press(self, x, y, button, modifiers):
""" Called when a mouse button is pressed. See pyglet docs for button
amd modifier mappings.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
button : int
Number representing mouse button that was clicked. 1 = left button,
4 = right button.
modifiers : int
Number representing any modifying keys that were pressed when the
mouse button was clicked.
"""
if self.exclusive:
vector = self.get_sight_vector()
block, previous = self.model.hit_test(self.position, vector)
if (button == mouse.RIGHT) or \
((button == mouse.LEFT) and (modifiers & key.MOD_CTRL)):
# ON OSX, control + left click = right click.
if previous:
self.model.add_block(previous, self.block)
elif button == pyglet.window.mouse.LEFT and block:
texture = self.model.world[block]
if texture != STONE:
self.model.remove_block(block)
else:
self.set_exclusive_mouse(True)
def on_mouse_motion(self, x, y, dx, dy):
""" Called when the player moves the mouse.
Parameters
----------
x, y : int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
dx, dy : float
The movement of the mouse.
"""
if self.exclusive:
m = 0.15
x, y = self.rotation
x, y = x + dx * m, y + dy * m
y = max(-90, min(90, y))
self.rotation = (x, y)
def on_key_press(self, symbol, modifiers):
""" Called when the player presses a key. See pyglet docs for key
mappings.
Parameters
----------
symbol : int
Number representing the key that was pressed.
modifiers : int
Number representing any modifying keys that were pressed.
"""
if symbol == key.W:
self.strafe[0] -= 1
elif symbol == key.S:
self.strafe[0] += 1
elif symbol == key.A:
self.strafe[1] -= 1
elif symbol == key.D:
self.strafe[1] += 1
elif symbol == key.SPACE:
if self.dy == 0:
self.dy = JUMP_SPEED
elif symbol == key.ESCAPE:
self.set_exclusive_mouse(False)
elif symbol == key.TAB:
self.flying = not self.flying
elif symbol in self.num_keys:
index = (symbol - self.num_keys[0]) % len(self.inventory)
self.block = self.inventory[index]
def on_key_release(self, symbol, modifiers):
""" Called when the player releases a key. See pyglet docs for key
mappings.
Parameters
----------
symbol : int
Number representing the key that was pressed.
modifiers : int
Number representing any modifying keys that were pressed.
"""
if symbol == key.W:
self.strafe[0] += 1
elif symbol == key.S:
self.strafe[0] -= 1
elif symbol == key.A:
self.strafe[1] += 1
elif symbol == key.D:
self.strafe[1] -= 1
def on_resize(self, width, height):
""" Called when the window is resized to a new `width` and `height`.
"""
# label
self.label.y = height - 10
# reticle
if self.reticle:
self.reticle.delete()
x, y = self.width // 2, self.height // 2
n = 10
self.reticle = pyglet.graphics.vertex_list(4,
('v2i', (x - n, y, x + n, y, x, y - n, x, y + n))
)
def set_2d(self):
""" Configure OpenGL to draw in 2d.
"""
width, height = self.get_size()
glDisable(GL_DEPTH_TEST)
viewport = self.get_viewport_size()
glViewport(0, 0, max(1, viewport[0]), max(1, viewport[1]))
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, max(1, width), 0, max(1, height), -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def set_3d(self):
""" Configure OpenGL to draw in 3d.
"""
width, height = self.get_size()
glEnable(GL_DEPTH_TEST)
viewport = self.get_viewport_size()
glViewport(0, 0, max(1, viewport[0]), max(1, viewport[1]))
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(65.0, width / float(height), 0.1, 60.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
x, y = self.rotation
glRotatef(x, 0, 1, 0)
glRotatef(-y, math.cos(math.radians(x)), 0, math.sin(math.radians(x)))
x, y, z = self.position
glTranslatef(-x, -y, -z)
def on_draw(self):
""" Called by pyglet to draw the canvas.
"""
self.clear()
self.set_3d()
glColor3d(1, 1, 1)
self.model.batch.draw()
self.draw_focused_block()
self.set_2d()
self.draw_label()
self.draw_reticle()
def draw_focused_block(self):
""" Draw black edges around the block that is currently under the
crosshairs.
"""
vector = self.get_sight_vector()
block = self.model.hit_test(self.position, vector)[0]
if block:
x, y, z = block
vertex_data = cube_vertices(x, y, z, 0.51)
glColor3d(0, 0, 0)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
pyglet.graphics.draw(24, GL_QUADS, ('v3f/static', vertex_data))
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
def draw_label(self):
""" Draw the label in the top left of the screen.
"""
x, y, z = self.position
self.label.text = '%02d (%.2f, %.2f, %.2f) %d / %d' % (
pyglet.clock.get_fps(), x, y, z,
len(self.model._shown), len(self.model.world))
self.label.draw()
def draw_reticle(self):
""" Draw the crosshairs in the center of the screen.
"""
glColor3d(0, 0, 0)
self.reticle.draw(GL_LINES)
def setup_fog():
""" Configure the OpenGL fog properties.
"""
# Enable fog. Fog "blends a fog color with each rasterized pixel fragment's
# post-texturing color."
glEnable(GL_FOG)
# Set the fog color.
glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1))
# Say we have no preference between rendering speed and quality.
glHint(GL_FOG_HINT, GL_DONT_CARE)
# Specify the equation used to compute the blending factor.
glFogi(GL_FOG_MODE, GL_LINEAR)
# How close and far away fog starts and ends. The closer the start and end,
# the denser the fog in the fog range.
glFogf(GL_FOG_START, 20.0)
glFogf(GL_FOG_END, 60.0)
def setup():
""" Basic OpenGL configuration.
"""
# Set the color of "clear", i.e. the sky, in rgba.
glClearColor(0.5, 0.69, 1.0, 1)
# Enable culling (not rendering) of back-facing facets -- facets that aren't
# visible to you.
glEnable(GL_CULL_FACE)
# Set the texture minification/magnification function to GL_NEAREST (nearest
# in Manhattan distance) to the specified texture coordinates. GL_NEAREST
# "is generally faster than GL_LINEAR, but it can produce textured 圖片
# with sharper edges because the transition between texture elements is not
# as smooth."
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
setup_fog()
def main():
window = Window(width=1800, height=1600, caption='Pyglet', resizable=True)
# Hide the mouse cursor and prevent the mouse from leaving the window.
window.set_exclusive_mouse(True)
setup()
pyglet.app.run()
if __name__ == '__main__':
main()

# coding: utf-8
'''
公眾號:一行資料,關注領取5T編程資料
'''
import sys
import pygame
import scene
import bullet
import food
import tanks
import home
from pygame.locals import *
# 開始界面顯示
def show_start_interface(screen, width, height):
tfont = pygame.font.Font('./font/simkai.ttf', width//4)
cfont = pygame.font.Font('./font/simkai.ttf', width//20)
title = tfont.render(u'坦克大戰', True, (255, 0, 0))
content1 = cfont.render(u'按1鍵進入單人游戲', True, (0, 0, 255))
content2 = cfont.render(u'按2鍵進入雙人人游戲', True, (0, 0, 255))
trect = title.get_rect()
trect.midtop = (width/2, height/4)
crect1 = content1.get_rect()
crect1.midtop = (width/2, height/1.8)
crect2 = content2.get_rect()
crect2.midtop = (width/2, height/1.6)
screen.blit(title, trect)
screen.blit(content1, crect1)
screen.blit(content2, crect2)
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
return 1
if event.key == pygame.K_2:
return 2
# 結束界面顯示
def show_end_interface(screen, width, height, is_win):
bg_img = pygame.image.load("./圖片/others/background.png")
screen.blit(bg_img, (0, 0))
if is_win:
font = pygame.font.Font('./font/simkai.ttf', width//10)
content = font.render(u'恭喜通關!', True, (255, 0, 0))
rect = content.get_rect()
rect.midtop = (width/2, height/2)
screen.blit(content, rect)
else:
fail_img = pygame.image.load("./圖片/others/gameover.png")
rect = fail_img.get_rect()
rect.midtop = (width/2, height/2)
screen.blit(fail_img, rect)
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
# 關卡切換
def show_switch_stage(screen, width, height, stage):
bg_img = pygame.image.load("./圖片/others/background.png")
screen.blit(bg_img, (0, 0))
font = pygame.font.Font('./font/simkai.ttf', width//10)
content = font.render(u'第%d關' % stage, True, (0, 255, 0))
rect = content.get_rect()
rect.midtop = (width/2, height/2)
screen.blit(content, rect)
pygame.display.update()
delay_event = pygame.constants.USEREVENT
pygame.time.set_timer(delay_event, 1000)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == delay_event:
return
# 主函式
def main():
# 初始化
pygame.init()
screen = pygame.display.set_mode((630, 630))
pygame.display.set_caption("坦克大戰")
# 加載圖片
bg_img = pygame.image.load("./圖片/others/background.png")
# 開始界面
num_player = show_start_interface(screen, 630, 630)
# 關卡
stage = 0
num_stage = 2
# 游戲是否結束
is_gameover = False
# 時鐘
clock = pygame.time.Clock()
# 主回圈
while not is_gameover:
# 關卡
stage += 1
if stage > num_stage:
break
show_switch_stage(screen, 630, 630, stage)
# 該關卡坦克總數量
enemytanks_total = min(stage * 18, 80)
# 場上存在的敵方坦克總數量
enemytanks_now = 0
# 場上可以存在的敵方坦克總數量
enemytanks_now_max = min(max(stage * 2, 4), 8)
# 精靈組
tanksGroup = pygame.sprite.Group()
mytanksGroup = pygame.sprite.Group()
enemytanksGroup = pygame.sprite.Group()
bulletsGroup = pygame.sprite.Group()
mybulletsGroup = pygame.sprite.Group()
enemybulletsGroup = pygame.sprite.Group()
myfoodsGroup = pygame.sprite.Group()
# 自定義事件
# -生成敵方坦克事件
genEnemyEvent = pygame.constants.USEREVENT + 0
pygame.time.set_timer(genEnemyEvent, 100)
# -敵方坦克靜止恢復事件
recoverEnemyEvent = pygame.constants.USEREVENT + 1
pygame.time.set_timer(recoverEnemyEvent, 8000)
# -我方坦克無敵恢復事件
noprotectMytankEvent = pygame.constants.USEREVENT + 2
pygame.time.set_timer(noprotectMytankEvent, 8000)
# 關卡地圖
map_stage = scene.Map(stage)
# 我方坦克
tank_player1 = tanks.myTank(1)
tanksGroup.add(tank_player1)
mytanksGroup.add(tank_player1)
if num_player > 1:
tank_player2 = tanks.myTank(2)
tanksGroup.add(tank_player2)
mytanksGroup.add(tank_player2)
is_switch_tank = True
player1_moving = False
player2_moving = False
# 為了輪胎的影片效果
time = 0
# 敵方坦克
for i in range(0, 3):
if enemytanks_total > 0:
enemytank = tanks.enemyTank(i)
tanksGroup.add(enemytank)
enemytanksGroup.add(enemytank)
enemytanks_now += 1
enemytanks_total -= 1
# 大本營
myhome = home.Home()
# 出場特效
appearance_img = pygame.image.load("./圖片/others/appear.png").convert_alpha()
appearances = []
appearances.append(appearance_img.subsurface((0, 0), (48, 48)))
appearances.append(appearance_img.subsurface((48, 0), (48, 48)))
appearances.append(appearance_img.subsurface((96, 0), (48, 48)))
# 關卡主回圈
while True:
if is_gameover is True:
break
if enemytanks_total < 1 and enemytanks_now < 1:
is_gameover = False
break
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == genEnemyEvent:
if enemytanks_total > 0:
if enemytanks_now < enemytanks_now_max:
enemytank = tanks.enemyTank()
if not pygame.sprite.spritecollide(enemytank, tanksGroup, False, None):
tanksGroup.add(enemytank)
enemytanksGroup.add(enemytank)
enemytanks_now += 1
enemytanks_total -= 1
if event.type == recoverEnemyEvent:
for each in enemytanksGroup:
each.can_move = True
if event.type == noprotectMytankEvent:
for each in mytanksGroup:
mytanksGroup.protected = False
# 檢查用戶鍵盤操作
key_pressed = pygame.key.get_pressed()
# 玩家一
# WSAD -> 上下左右
# 空格鍵射擊
if key_pressed[pygame.K_w]:
tanksGroup.remove(tank_player1)
tank_player1.move_up(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player1)
player1_moving = True
elif key_pressed[pygame.K_s]:
tanksGroup.remove(tank_player1)
tank_player1.move_down(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player1)
player1_moving = True
elif key_pressed[pygame.K_a]:
tanksGroup.remove(tank_player1)
tank_player1.move_left(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player1)
player1_moving = True
elif key_pressed[pygame.K_d]:
tanksGroup.remove(tank_player1)
tank_player1.move_right(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player1)
player1_moving = True
elif key_pressed[pygame.K_SPACE]:
if not tank_player1.bullet.being:
tank_player1.shoot()
# 玩家二
# ↑↓←→ -> 上下左右
# 小鍵盤0鍵射擊
if num_player > 1:
if key_pressed[pygame.K_UP]:
tanksGroup.remove(tank_player2)
tank_player2.move_up(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player2)
player2_moving = True
elif key_pressed[pygame.K_DOWN]:
tanksGroup.remove(tank_player2)
tank_player2.move_down(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player2)
player2_moving = True
elif key_pressed[pygame.K_LEFT]:
tanksGroup.remove(tank_player2)
tank_player2.move_left(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player2)
player2_moving = True
elif key_pressed[pygame.K_RIGHT]:
tanksGroup.remove(tank_player2)
tank_player2.move_right(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(tank_player2)
player2_moving = True
elif key_pressed[pygame.K_KP0]:
if not tank_player2.bullet.being:
tank_player2.shoot()
# 背景
screen.blit(bg_img, (0, 0))
# 石頭墻
for each in map_stage.brickGroup:
screen.blit(each.brick, each.rect)
# 鋼墻
for each in map_stage.ironGroup:
screen.blit(each.iron, each.rect)
# 冰
for each in map_stage.iceGroup:
screen.blit(each.ice, each.rect)
# 河流
for each in map_stage.riverGroup:
screen.blit(each.river, each.rect)
# 樹
for each in map_stage.treeGroup:
screen.blit(each.tree, each.rect)
time += 1
if time == 5:
time = 0
is_switch_tank = not is_switch_tank
# 我方坦克
if tank_player1 in mytanksGroup:
if is_switch_tank and player1_moving:
screen.blit(tank_player1.tank_0, (tank_player1.rect.left, tank_player1.rect.top))
player1_moving = False
else:
screen.blit(tank_player1.tank_1, (tank_player1.rect.left, tank_player1.rect.top))
if tank_player1.protected:
screen.blit(tank_player1.protected_mask1, (tank_player1.rect.left, tank_player1.rect.top))
if num_player > 1:
if tank_player2 in mytanksGroup:
if is_switch_tank and player2_moving:
screen.blit(tank_player2.tank_0, (tank_player2.rect.left, tank_player2.rect.top))
player1_moving = False
else:
screen.blit(tank_player2.tank_1, (tank_player2.rect.left, tank_player2.rect.top))
if tank_player2.protected:
screen.blit(tank_player1.protected_mask1, (tank_player2.rect.left, tank_player2.rect.top))
# 敵方坦克
for each in enemytanksGroup:
# 出生特效
if each.born:
if each.times > 0:
each.times -= 1
if each.times <= 10:
screen.blit(appearances[2], (3+each.x*12*24, 3))
elif each.times <= 20:
screen.blit(appearances[1], (3+each.x*12*24, 3))
elif each.times <= 30:
screen.blit(appearances[0], (3+each.x*12*24, 3))
elif each.times <= 40:
screen.blit(appearances[2], (3+each.x*12*24, 3))
elif each.times <= 50:
screen.blit(appearances[1], (3+each.x*12*24, 3))
elif each.times <= 60:
screen.blit(appearances[0], (3+each.x*12*24, 3))
elif each.times <= 70:
screen.blit(appearances[2], (3+each.x*12*24, 3))
elif each.times <= 80:
screen.blit(appearances[1], (3+each.x*12*24, 3))
elif each.times <= 90:
screen.blit(appearances[0], (3+each.x*12*24, 3))
else:
each.born = False
else:
if is_switch_tank:
screen.blit(each.tank_0, (each.rect.left, each.rect.top))
else:
screen.blit(each.tank_1, (each.rect.left, each.rect.top))
if each.can_move:
tanksGroup.remove(each)
each.move(tanksGroup, map_stage.brickGroup, map_stage.ironGroup, myhome)
tanksGroup.add(each)
# 我方子彈
for tank_player in mytanksGroup:
if tank_player.bullet.being:
tank_player.bullet.move()
screen.blit(tank_player.bullet.bullet, tank_player.bullet.rect)
# 子彈碰撞敵方子彈
for each in enemybulletsGroup:
if each.being:
if pygame.sprite.collide_rect(tank_player.bullet, each):
tank_player.bullet.being = False
each.being = False
enemybulletsGroup.remove(each)
break
else:
enemybulletsGroup.remove(each)
# 子彈碰撞敵方坦克
for each in enemytanksGroup:
if each.being:
if pygame.sprite.collide_rect(tank_player.bullet, each):
if each.is_red == True:
myfood = food.Food()
myfood.generate()
myfoodsGroup.add(myfood)
each.is_red = False
each.blood -= 1
each.color -= 1
if each.blood < 0:
each.being = False
enemytanksGroup.remove(each)
enemytanks_now -= 1
tanksGroup.remove(each)
else:
each.reload()
tank_player.bullet.being = False
break
else:
enemytanksGroup.remove(each)
tanksGroup.remove(each)
# 子彈碰撞石頭墻
if pygame.sprite.spritecollide(tank_player.bullet, map_stage.brickGroup, True, None):
tank_player.bullet.being = False
'''
# 等價方案(更科學點)
for each in map_stage.brickGroup:
if pygame.sprite.collide_rect(tank_player.bullet, each):
tank_player.bullet.being = False
each.being = False
map_stage.brickGroup.remove(each)
break
'''
# 子彈碰鋼墻
if tank_player.bullet.stronger:
if pygame.sprite.spritecollide(tank_player.bullet, map_stage.ironGroup, True, None):
tank_player.bullet.being = False
else:
if pygame.sprite.spritecollide(tank_player.bullet, map_stage.ironGroup, False, None):
tank_player.bullet.being = False
'''
# 等價方案(更科學點)
for each in map_stage.ironGroup:
if pygame.sprite.collide_rect(tank_player.bullet, each):
tank_player.bullet.being = False
if tank_player.bullet.stronger:
each.being = False
map_stage.ironGroup.remove(each)
break
'''
# 子彈碰大本營
if pygame.sprite.collide_rect(tank_player.bullet, myhome):
tank_player.bullet.being = False
myhome.set_dead()
is_gameover = True
# 敵方子彈
for each in enemytanksGroup:
if each.being:
if each.can_move and not each.bullet.being:
enemybulletsGroup.remove(each.bullet)
each.shoot()
enemybulletsGroup.add(each.bullet)
if not each.born:
if each.bullet.being:
each.bullet.move()
screen.blit(each.bullet.bullet, each.bullet.rect)
# 子彈碰撞我方坦克
for tank_player in mytanksGroup:
if pygame.sprite.collide_rect(each.bullet, tank_player):
if not tank_player.protected:
tank_player.life -= 1
if tank_player.life < 0:
mytanksGroup.remove(tank_player)
tanksGroup.remove(tank_player)
if len(mytanksGroup) < 1:
is_gameover = True
else:
tank_player.reset()
each.bullet.being = False
enemybulletsGroup.remove(each.bullet)
break
# 子彈碰撞石頭墻
if pygame.sprite.spritecollide(each.bullet, map_stage.brickGroup, True, None):
each.bullet.being = False
enemybulletsGroup.remove(each.bullet)
'''
# 等價方案(更科學點)
for one in map_stage.brickGroup:
if pygame.sprite.collide_rect(each.bullet, one):
each.bullet.being = False
one.being = False
enemybulletsGroup.remove(one)
break
'''
# 子彈碰鋼墻
if each.bullet.stronger:
if pygame.sprite.spritecollide(each.bullet, map_stage.ironGroup, True, None):
each.bullet.being = False
else:
if pygame.sprite.spritecollide(each.bullet, map_stage.ironGroup, False, None):
each.bullet.being = False
'''
# 等價方案(更科學點)
for one in map_stage.ironGroup:
if pygame.sprite.collide_rect(each.bullet, one):
each.bullet.being = False
if each.bullet.stronger:
one.being = False
map_stage.ironGroup.remove(one)
break
'''
# 子彈碰大本營
if pygame.sprite.collide_rect(each.bullet, myhome):
each.bullet.being = False
myhome.set_dead()
is_gameover = True
else:
enemytanksGroup.remove(each)
tanksGroup.remove(each)
# 家
screen.blit(myhome.home, myhome.rect)
# 食物
for myfood in myfoodsGroup:
if myfood.being and myfood.time > 0:
screen.blit(myfood.food, myfood.rect)
myfood.time -= 1
for tank_player in mytanksGroup:
if pygame.sprite.collide_rect(tank_player, myfood):
# 消滅當前所有敵人
if myfood.kind == 0:
enemytanksGroup = pygame.sprite.Group()
enemytanks_total -= enemytanks_now
enemytanks_now = 0
# 敵人靜止
if myfood.kind == 1:
for each in enemytanksGroup:
each.can_move = False
# 子彈增強
if myfood.kind == 2:
tank_player.bullet.stronger = True
# 使得大本營的墻變為鋼板
if myfood.kind == 3:
map_stage.protect_home()
# 坦克獲得一段時間的保護罩
if myfood.kind == 4:
for tank_player in mytanksGroup:
tank_player.protected = True
# 坦克升級
if myfood.kind == 5:
tank_player.up_level()
# 坦克生命+1
if myfood.kind == 6:
tank_player.life += 1
myfood.being = False
myfoodsGroup.remove(myfood)
break
else:
myfood.being = False
myfoodsGroup.remove(myfood)
pygame.display.flip()
clock.tick(60)
if not is_gameover:
show_end_interface(screen, 630, 630, True)
else:
show_end_interface(screen, 630, 630, False)
if __name__ == '__main__':
main()
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/290886.html
標籤:python
上一篇:python:洗掉串列中重復元素
