04.碰撞反應

碰撞檢查   兩精靈之間的距離<=精靈顯示圖像寬度一半的和,就說明碰撞了react

實施碰撞功能:

咱們將兩點間的距離等經常使用函數抽取成一個util模塊app

import math


def distance(point_1=(0, 0), point_2=(0, 0)):
    """計算兩點間的距離"""
    return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)


def center_image(image):
    """錨點居中"""
    image.anchor_x = image.width / 2
    image.anchor_y = image.height / 2

在Physicalobject類構造方法中增長屬性dead表示是否死亡dom

 # 是否死亡
 self.dead = False

接着添加兩個方法:ide

 def collides_with(self, other_object):
     """是否與other_object碰撞"""
      collision_distance = self.image.width / 2 + other_object.image.width / 2
      actual_distance = util.distance(self.position, other_object.position)
      return (actual_distance <= collision_distance)

def handle_collision_with(self, other_object):
    """碰撞處理
       每一個對象在碰到另外一個對象時就死掉
    """
    self.dead = True

接下來就是對遊戲對象列表遍歷移除死亡對象game_objects,在修改啓動模塊的update()函數

def update(dt):
    for obj in game_objects:
        obj.update(dt)  # 調用Physicalobject的更新
    # 檢查全部對象對
    for i in range(len(game_objects)):
        for j in range(i + 1, len(game_objects)):
            obj_1 = game_objects[i]
            obj_2 = game_objects[j]
            if not obj_1.dead and not obj_2.dead:
                if obj_1.collides_with(obj_2):
                    obj_1.handle_collision_with(obj_2)
                    obj_2.handle_collision_with(obj_1)

    for to_remove in [obj for obj in game_objects if obj.dead]:
        to_remove.delete()
        game_objects.remove(to_remove)

 

如今能夠運行查看,兩精靈碰撞會都消失測試

碰撞響應:

如今須要咱們在遊戲過程當中開始將內容添加到game_objects列表中,並讓對象檢查彼此的類型以決定是否應該死亡spa

 在循環下方聲明一個to_add列表,而後向其中添加新對象。在update()最底部,在刪除對象代碼以後,將to_add中的對象添加到game_objects中:code

def update(dt):
    # 檢查全部對象對
    for i in range(len(game_objects)):
        for j in range(i + 1, len(game_objects)):
            obj_1 = game_objects[i]
            obj_2 = game_objects[j]
            if not obj_1.dead and not obj_2.dead:
                if obj_1.collides_with(obj_2):
                    obj_1.handle_collision_with(obj_2)
                    obj_2.handle_collision_with(obj_1)

    to_add = []
    for obj in game_objects:
        obj.update(dt)  # 調用Physicalobject的更新
        to_add.extend(obj.new_objects)
        obj.new_objects = [] # 添加完置空

    for to_remove in [obj for obj in game_objects if obj.dead]:
        # 若是垂死的對象產生了任何新對象,請稍後將它們添加到game_objects列表中
        to_add.extend(obj.new_objects)
        to_remove.delete()
        game_objects.remove(to_remove)
    game_objects.extend(to_add)

Physicalobject類構造中添加new_objects屬性:對象

self.new_objects = []

下面來完成子彈模塊:blog

"""子彈bullet.py"""
import pyglet
from let import physicalobject3, resources


class Bullet(physicalobject3.Physicalobject):
    def __init__(self, *args, **kwargs):
        super().__init__(resources.bullet_image, *args, **kwargs)
        # 執行一次
        pyglet.clock.schedule_once(self.die, 0.5)
        self.is_bullet = True  # 是子彈

    def die(self, dt):
        self.dead = True

發射子彈:

Player類,構造中定義子彈的速度:
 # 子彈速度
 self.bullet_speed = 700.0

發射子彈的按鍵控制:

    def on_key_press(self, symbol, modifiers):
        if symbol == key.SPACE:
            self.fire()

咱們須要在船頭而不是在船頭放出子彈。咱們還須要將飛船的現有速度添加到子彈的新速度中,不然,若是玩家走得足夠快,子彈的運行速度最終會比飛船慢

 def fire(self):
        """將飛船的現有速度添加到子彈的新速度中"""
        # 子彈的大小
        angle_radians = -math.radians(self.rotation)  # 轉換爲弧度並反轉方向
        ship_radius = self.image.width / 2
        bullet_x = self.x + math.cos(angle_radians) * ship_radius
        bullet_y = self.y + math.sin(angle_radians) * ship_radius
        # 子彈
        new_bullet = bullet.Bullet(bullet_x, bullet_y, batch=self.batch)
        # 飛船的現有速度+子彈的速度=子彈的新速度
        bullet_vx = self.velocity_x + math.cos(angle_radians) * self.bullet_speed
        bullet_vy = self.velocity_y + math.sin(angle_radians) * self.bullet_speed
        new_bullet.velocity_x, new_bullet.velocity_y = bullet_vx, bullet_vy
        self.new_objects.append(new_bullet)

player構造中新增event_handlers屬性以下,這樣就包含了放子彈事件與KeyStateHandler

self.event_handlers = [self, self.key_handler]

主模塊中修改原來的推送事件

# 將其推送到事件堆棧中
for handler in play_ship.event_handlers:
    game_window.push_handlers(handler)

碰撞時不該破壞相同類型的對象,修改Physicalobject類中的handle_collision_with:

    def handle_collision_with(self, other_object):
        """碰撞處理
        碰撞時不該破壞相同類型的對象
        """
        if other_object.__class__ == self.__class__:
            self.dead = False
        else:
            self.dead = True

如今還有一個問題是,子彈與玩家碰撞,咱們在Physicalobject構造中定義reacts_to_bullets,表示是否對子彈有反應碰撞,並默認爲不是子彈:

self.is_bullet = False  # 不是子彈
self.reacts_to_bullets = True # 對子彈有反應

顯然,玩家構造中設置爲False

 self.reacts_to_bullets = False  # 對子彈無反應

而後咱們修改collides_with碰撞檢測:

    def collides_with(self, other_object):
        """是否與other_object碰撞"""
        if not self.reacts_to_bullets and other_object.is_bullet:
            return False
        if self.is_bullet and not other_object.reacts_to_bullets:
            return False
        collision_distance = self.image.width / 2 + other_object.image.width / 2
        actual_distance = util.distance(self.position, other_object.position)
        return (actual_distance <= collision_distance)

ok,如今讓咱們讓子彈擊中小行星吧

下面咱們增長遊戲難度,擊中小行星,小行星會分裂:

編寫小行星類:

"""小行星類aster.py"""
import random
from let import resources, physicalobject4


class AsteroidSmall(physicalobject4.Physicalobject):
    def __init__(self, *args, **kwargs):
        super(AsteroidSmall, self).__init__(resources.asteroid_image, *args, **kwargs)
        self.rotate_speed = random.random() * 100.0 - 50.0  # 隨機旋轉

    def handle_collision_with(self, other_object): # 碰撞處理
        super(AsteroidSmall, self).handle_collision_with(other_object)
        if self.dead and self.scale > 0.25:  # 當小行星的大小是新小行星的1/4時,小行星應該中止分裂
            num_asteroids = random.randint(2, 3)
            for i in range(num_asteroids):
                new_asteroid = AsteroidSmall(x=self.x, y=self.y, batch=self.batch)
                new_asteroid.rotation = random.randint(0, 360)
                new_asteroid.velocity_x = (random.random() * 70 + self.velocity_x)
                new_asteroid.velocity_y = (random.random() * 70 + self.velocity_y)
                new_asteroid.scale = self.scale * 0.5
                self.new_objects.append(new_asteroid)

    def update(self, dt):
        super(AsteroidSmall, self).update(dt)
        self.rotation += self.rotate_speed * dt

接下來須要作的最後一件事是轉到load.py並讓asteroid()方法建立一個新的Asteroid而不是PhysicalObject:

new_asteroid = aster.AsteroidSmall(x=asteroid_x, y=asteroid_y, batch=batch)
def asteroids(num_asteroids, player_position, batch=None):
    ...
    for i in range(num_asteroids):
        ...
        new_asteroid = aster.AsteroidSmall(x=asteroid_x, y=asteroid_y, batch=batch)
        ...
    return asteroids

最後測試:

相關文章
相關標籤/搜索