为什么我的球会粘在一起?

Shi*_*tia 9 python pygame

我正在制作一个移动游戏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()
Run Code Online (Sandbox Code Playgroud)

编辑:忘记添加矢量类.

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)
Run Code Online (Sandbox Code Playgroud)

Sno*_*ing 7

对不起,没有复制,你的球很好: 在此输入图像描述

不,但你遇到的问题是可变对象.当你设置

ball.moveVector = player.shootVector
Run Code Online (Sandbox Code Playgroud)

你将所有moveVector的东西都设置在同一个物体上,所以每次碰撞检测都会同时改变所有球的方向.最简单的修复:

ball.moveVector = player.shootVector + Vector(x=0, y=0)
Run Code Online (Sandbox Code Playgroud)

编辑

我使用了不同的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)
Run Code Online (Sandbox Code Playgroud)

(这是在Vector课堂内,同样适用于减法和多重.)

结束编辑

当球离开图像时你的重置方式也存在一些问题,你应该防止玩家再次点击直到重置球,但我想这会在以后的开发中出现.

附录

注意:我在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()
Run Code Online (Sandbox Code Playgroud)

  • 如果你想坚持你的自定义`Vector`,我上面添加了一个修复,我认为这对学习者来说比使用预先实现的东西更有趣,所以请随意这样做! (2认同)