我正在制作Ballz的克隆版,这是一款手机游戏,你需要在多次击中后在一些块上拍摄一大堆球。就像BrickBreaker类固醇一样。我大部分时间都在工作,但我无法弄清楚如何一个接一个地射球。我从测试中知道,在拍摄时,球在不同的地方,但在此之后它们会占据相同的空间。
哦,顺便说一句,我将它们分开的方式是让球再次回到屏幕之外。所以你可以想象它就像把它们全部放在一个接一个,在屏幕外,在播放器底部之下。
这是我的代码:
import pygame
import math
import random
from vector import *
backgroundColor = (0, 0, 0)
ballColor = (255, 255, 255)
sizeOfOneBlock = 50.0
realDimension = 600.0
blockNumberInLine = int(realDimension/sizeOfOneBlock)
size = [int(realDimension), int(realDimension)]
# eg. probability(1/3)
def probability(chance):
return random.random() <= chance
def abs(x):
if x>=0:
return x
else:
return -x
# the classes used:
# Block, BlockHandler, Ball, Player
class Block():
def __init__(self, strength, i, j):
self.strength = strength
# i and j are numbers between 0 and blockNumberInLine-1
self.i, self.j = i, j
self.refreshStats()
def refreshStats(self):
self.color = (100, 224, 89)
def display(self, Surface):
pygame.draw.rect(Surface, (0, 0, 255), (self.i*sizeOfOneBlock, self.j*sizeOfOneBlock, sizeOfOneBlock, sizeOfOneBlock), 0)
class BlockHandler():
def __init__(self):
self.blockList = []
self.blockPositions = []
def resetPositionArray(self):
self.blockPositions = []
for block in self.blockList:
self.blockPositions.append([block.i*sizeOfOneBlock, block.j*sizeOfOneBlock])
def addNewLayer(self, gameLevel):
# move every existing block down
for block in self.blockList:
block.j += 1
# add new layer
for i in range(blockNumberInLine):
if probability(1/3):
# gameLevel determines the strength of the block
self.blockList.append(Block(gameLevel, i, 0))
# after all blocks are loaded, do this
self.resetPositionArray()
def displayBlocks(self, Surface):
for block in self.blockList:
block.display(Surface)
class Ball():
def __init__(self, posVector, moveVector):
self.posVector = posVector
self.moveVector = moveVector
self.radius = 2
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
def move(self):
self.posVector.add(self.moveVector)
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
def display(self, Surface):
pygame.draw.circle(Surface, ballColor, (self.x, self.y), self.radius)
def changeDirection(self, tuple):
# east
if tuple[0]>0:
self.moveVector.x = abs(self.moveVector.x)
# west
if tuple[0]<0:
self.moveVector.x = -abs(self.moveVector.x)
# south
if tuple[1]>0:
self.moveVector.y = abs(self.moveVector.y)
# north
if tuple[1]<0:
self.moveVector.y = -abs(self.moveVector.y)
def collisionDetect(self, blockX, blockY, blockSize, circleX, circleY, circleRadius):
xDeflect, yDeflect = 0, 0
# if in the same column
if (circleX>=blockX) and (circleX<=(blockX+blockSize)):
# if touching block from above or below
distance = circleY-(blockY+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
# either 1 or -1
if distance!=0:
yDeflect = distance/abs(distance)
# if in the same row
if (circleY>=blockY) and (circleY<=(blockY+blockSize)):
# if touching block from left or right
distance = circleX-(blockX+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
if distance!=0:
xDeflect = distance/abs(distance)
return [xDeflect, yDeflect]
def checkForCollisions(self, blockPositions):
# walls
if (self.x<=(0+self.radius)):
# east
self.changeDirection([1,0])
if (self.x>=(realDimension-self.radius)):
# west
self.changeDirection([-1,0])
if (self.y<=(0+self.radius)):
# south
self.changeDirection([0,1])
# blocks
for pos in blockPositions:
collision = self.collisionDetect(pos[0], pos[1], sizeOfOneBlock, self.x, self.y, self.radius)
self.changeDirection(collision)
class Player():
def __init__(self, posVector):
self.posVector = posVector
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
self.level = 1
self.numberOfBalls = 3
self.balls = []
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
self.balls.append(Ball(self.posVector, moveVector=Vector(0.0, 0.0)))
# print(ball)
def placeBalls(self, separateVector):
# self.resetBalls()
for j in range(len(self.balls)):
ball = self.balls[j]
for i in range(j):
ball.posVector.subtract(separateVector)
def display(self, Surface):
# possibly change color
pygame.draw.circle(Surface, ballColor, (self.x, self.y), 20)
def displayBalls(self, Surface):
for ball in self.balls:
ball.display(Surface)
def updateBalls(self, blockHandler):
for ball in self.balls:
ball.move()
ball.checkForCollisions(blockPositions=blockHandler.blockPositions)
def main():
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Ballz")
done = False
clock = pygame.time.Clock()
blockHandler = BlockHandler()
blockHandler.addNewLayer(1)
playerPosition = Vector(realDimension/2, realDimension-10)
player = Player(posVector=playerPosition)
player.resetBalls()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
# JFF
if event.key == pygame.K_w:
blockHandler.addNewLayer(1)
# for debugging
if event.key == pygame.K_d:
for ball in player.balls:
print(ball.posVector.x, ball.posVector.y)
print(ball.moveVector.x, ball.moveVector.y)
print("")
if event.key == pygame.K_r:
player.resetBalls()
if event.type == pygame.MOUSEBUTTONUP:
mousePos = pygame.mouse.get_pos()
player.shootVector = Vector(mousePos[0]-player.x, mousePos[1]-player.y).shortenTo(1)
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector.subtract(player.shootVector)
ball.moveVector = player.shootVector
# test
print(ball.posVector.x, ball.posVector.y)
print(ball.moveVector.x, ball.moveVector.y)
print("")
# LOGIC
player.updateBalls(blockHandler)
# DRAW
screen.fill(backgroundColor)
blockHandler.displayBlocks(screen)
player.displayBalls(screen)
player.display(screen)
pygame.display.flip()
# 60 frames per second
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()
编辑:忘记添加矢量类。
class Vector():
def __init__(self, x=0, y=0):
self.x, self.y = x, y
def magnitude(self):
return ((self.x)**2 + (self.y)**2)**0.5
def shortenTo(self, radius):
magnitude = self.magnitude()
unitX = self.x/magnitude
unitY = self.y/magnitude
return Vector(unitX*radius, unitY*radius)
def add(self, addedVector):
self.x += addedVector.x
self.y += addedVector.y
def subtract(self, subtractedVector):
self.x -= subtractedVector.x
self.y -= subtractedVector.y
def printCoordinates(self):
print(self.x, self.y)
答案 0 :(得分:7)
不,但你遇到的问题是可变对象。 当你设置
ball.moveVector = player.shootVector
您将所有moveVector
设置为同一个对象,因此每次碰撞检测都会同时改变所有球的方向。最简单的解决方法:
ball.moveVector = player.shootVector + Vector(x=0, y=0)
我使用了不同的vector
模块,您可以使用copy.copy
或创建自定义__add__
方法:
def __add__(self, other):
if not isinstance(other, Vector)
raise ValueError
return Vector(self.x+other.x, self.y+other.y)
(它来自Vector
类,同样适用于减法和mult。)
当球离开图像时你重置的方式也存在一些问题,你应该防止玩家再次点击,直到重置球,但我想这会在以后的开发中出现。
注意:我在Python 3中工作,要么我安装了不同的vector
模块,要么他们改变了很多,所以不得不在那里更改一些语法。希望它有所帮助:)
import pygame
import math
import random
from vector import *
backgroundColor = (0, 0, 0)
ballColor = (255, 255, 255)
sizeOfOneBlock = 50.0
realDimension = 600.0
blockNumberInLine = int(realDimension/sizeOfOneBlock)
size = [int(realDimension), int(realDimension)]
# eg. probability(1/3)
def probability(chance):
return random.random() <= chance
def abs(x):
if x>=0:
return x
else:
return -x
# the classes used:
# Block, BlockHandler, Ball, Player
class Block():
def __init__(self, strength, i, j):
self.strength = strength
# i and j are numbers between 0 and blockNumberInLine-1
self.i, self.j = i, j
self.refreshStats()
def refreshStats(self):
self.color = (100, 224, 89)
def display(self, Surface):
pygame.draw.rect(Surface, (0, 0, 255), (self.i*sizeOfOneBlock, self.j*sizeOfOneBlock, sizeOfOneBlock, sizeOfOneBlock), 0)
class BlockHandler():
def __init__(self):
self.blockList = []
self.blockPositions = []
def resetPositionArray(self):
self.blockPositions = []
for block in self.blockList:
self.blockPositions.append([block.i*sizeOfOneBlock, block.j*sizeOfOneBlock])
def addNewLayer(self, gameLevel):
# move every existing block down
for block in self.blockList:
block.j += 1
# add new layer
for i in range(blockNumberInLine):
if probability(1/3):
# gameLevel determines the strength of the block
self.blockList.append(Block(gameLevel, i, 0))
# after all blocks are loaded, do this
self.resetPositionArray()
def displayBlocks(self, Surface):
for block in self.blockList:
block.display(Surface)
class Ball():
def __init__(self, posVector, moveVector):
self.posVector = posVector
self.moveVector = moveVector
self.radius = 2
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
def move(self):
self.posVector += self.moveVector
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
def display(self, Surface):
pygame.draw.circle(Surface, ballColor, (self.x, self.y), self.radius)
def changeDirection(self, tuple):
# east
if tuple[0]>0:
self.moveVector['x'] = abs(self.moveVector['x'])
# west
if tuple[0]<0:
self.moveVector['x'] = -abs(self.moveVector['x'])
# south
if tuple[1]>0:
self.moveVector['y'] = abs(self.moveVector['y'])
# north
if tuple[1]<0:
self.moveVector['y'] = -abs(self.moveVector['y'])
def collisionDetect(self, blockX, blockY, blockSize, circleX, circleY, circleRadius):
xDeflect, yDeflect = 0, 0
# if in the same column
if (circleX>=blockX) and (circleX<=(blockX+blockSize)):
# if touching block from above or below
distance = circleY-(blockY+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
# either 1 or -1
if distance!=0:
yDeflect = distance/abs(distance)
# if in the same row
if (circleY>=blockY) and (circleY<=(blockY+blockSize)):
# if touching block from left or right
distance = circleX-(blockX+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
if distance!=0:
xDeflect = distance/abs(distance)
return [xDeflect, yDeflect]
def checkForCollisions(self, blockPositions):
# walls
if (self.x<=(0+self.radius)):
# east
self.changeDirection([1,0])
if (self.x>=(realDimension-self.radius)):
# west
self.changeDirection([-1,0])
if (self.y<=(0+self.radius)):
# south
self.changeDirection([0,1])
# blocks
for pos in blockPositions:
collision = self.collisionDetect(pos[0], pos[1], sizeOfOneBlock, self.x, self.y, self.radius)
self.changeDirection(collision)
class Player():
def __init__(self, posVector):
self.posVector = posVector
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
self.level = 1
self.numberOfBalls = 3
self.balls = []
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
x = Vector(x=j, y=j) - Vector(x=j, y=j)
self.balls.append(Ball(self.posVector, x))
# print(ball)
def placeBalls(self, separateVector):
# self.resetBalls()
for j in range(len(self.balls)):
ball = self.balls[j]
for i in range(j):
ball.posVector -= separateVector
def display(self, Surface):
# possibly change color
pygame.draw.circle(Surface, ballColor, (self.x, self.y), 20)
def displayBalls(self, Surface):
for ball in self.balls:
ball.display(Surface)
def updateBalls(self, blockHandler):
for ball in self.balls:
ball.move()
ball.checkForCollisions(blockPositions=blockHandler.blockPositions)
def main():
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Ballz")
done = False
clock = pygame.time.Clock()
blockHandler = BlockHandler()
blockHandler.addNewLayer(1)
playerPosition = Vector(x=realDimension/2, y=realDimension-10)
player = Player(posVector=playerPosition)
player.resetBalls()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
# JFF
if event.rrrr == pygame.K_w:
blockHandler.addNewLayer(1)
# for debugging
if event.key == pygame.K_d:
for ball in player.balls:
print(ball.posVector['x'], ball.posVector['y'])
print(ball.moveVector['x'], ball.moveVector['y'])
print("")
if event.key == pygame.K_r:
player.resetBalls()
if event.type == pygame.MOUSEBUTTONUP:
mousePos = pygame.mouse.get_pos()
player.shootVector = Vector(x=mousePos[0]-player.x, y=mousePos[1]-player.y) / ((mousePos[0]-player.x)**2 + (mousePos[1]-player.y))**.5
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector -= player.shootVector
ball.moveVector = player.shootVector + Vector(x=0, y=0)
# test
print(ball.posVector['x'], ball.posVector['y'])
print(ball.moveVector['x'], ball.moveVector['y'])
print("")
# LOGIC
player.updateBalls(blockHandler)
# DRAW
screen.fill(backgroundColor)
blockHandler.displayBlocks(screen)
player.displayBalls(screen)
player.display(screen)
pygame.display.flip()
# 60 frames per second
clock.tick(60)
main()
答案 1 :(得分:4)
由于您已将玩家self.posVector
传递给球类实例,然后将其分配到posVector
属性,因此玩家和球的位置均指内存中相同的Vector
对象。您可以改为创建向量的副本,例如使用copy
模块,以便每个ball.posVector
引用一个单独的向量对象。
首先导入复制功能(它会创建浅拷贝)。
from copy import copy
然后在传递之前复制矢量对象。
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
self.balls.append(
Ball(copy(self.posVector), moveVector=Vector(0.0, 0.0)))
# ...
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector.subtract(player.shootVector)
ball.moveVector = copy(player.shootVector)
我还建议使用pygame的Vector2课程,而不是您自己的Vector
课程,因为它功能更丰富,效率更高。
abs
是一个内置函数。