碰撞檢查 兩精靈之間的距離<=精靈顯示圖像寬度一半的和,就說明碰撞了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
最後測試: