python/pygame 挑战魂斗罗 笔记(三)

发布于:2024-04-25 ⋅ 阅读:(35) ⋅ 点赞:(0)

感觉最难的部分已经解决了,下面开始发射子弹。

一、建立ContraBullet.py文件,Bullit类:

1、设定子弹速度

Config.py中设定子弹移动速度为常量Constant.BULLET_SPEED = 8。

2、载入子弹图片:

图片也是6张,子弹发出后逐渐变大(这是为什么呢)?同样建立bullet_list列表和bullet_number索引。载入图片后获取rect属性再按照人物放大比例放大,加到子弹列表中。

3、子弹图片索引

设置一个计数器变量counter,随着循环增加到30,也就是1/2个FPS,就让子弹图片索引增加1,改变子弹图片样式。

import os.path

import pygame

from Config import Constant


class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.bullet_list = []
        self.bullet_number = 0
        for i in range(6):
            image = pygame.image.load(os.path.join('image', 'bullet', 'bullet' + str(i + 1) + '.png'))
            rect = image.get_rect()
            image = pygame.transform.scale(
                image, (rect.width * Constant.PLAYER_SCALE, rect.height * Constant.PLAYER_SCALE))
            self.bullet_list.append(image)
        self.image = self.bullet_list[self.bullet_number]
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y
        self.speed_x = 0
        self.speed_y = 0

        self.counter = 0

    def update(self):
        if self.counter >= 30:
            self.bullet_number += 1
            if self.bullet_number > 5:
                self.bullet_number = 0
        else:
            self.counter += 1
        self.image = self.bullet_list[self.bullet_number]
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y

二、Bill类增加按键判定以及shoot发射方法:

1、设定k键为发射子弹以及子弹发射间隔时间、子弹x、y方向移速和不同角度子弹发射位置等变量。
            self.bullet_time = 0
            self.bullet_speed_x = 0
            self.bullet_speed_y = 0
            self.bullet_x = self.rect.right
            self.bullet_y = self.rect.y + 11.5 * Constant.PLAYER_SCALE
2、建立shoot发射子弹方法:

主要是理清各种方向、状态下子弹的发射位置和x、y方向的速度。

    def shoot(self):
        if self.direction == 'right':
            self.bullet_speed_x = Constant.BULLET_SPEED
            self.bullet_x = self.rect.right
        elif self.direction == 'left':
            self.bullet_speed_x = -Constant.BULLET_SPEED
            self.bullet_x = self.rect.left

        if self.image_type == 'stand':
            self.bullet_y = self.rect.y + 11.5 * Constant.PLAYER_SCALE
            self.bullet_speed_y = 0
        elif self.image_type == 'down':
            self.bullet_y = self.rect.y + 24 * Constant.PLAYER_SCALE
            self.bullet_speed_y = 0
        elif self.image_type == 'up':
            self.bullet_y = self.rect.y
            self.bullet_speed_x = 0
            self.bullet_speed_y = -Constant.BULLET_SPEED
        elif self.image_type == 'oblique_down':
            self.bullet_y = self.rect.y + 20.5 * Constant.PLAYER_SCALE
            self.bullet_speed_y = Constant.BULLET_SPEED
        elif self.image_type == 'oblique_up':
            self.bullet_y = self.rect.y
            self.bullet_speed_y = -Constant.BULLET_SPEED

        if self.bullet_time >= 30:
            bill_bullet = ContraBullet.Bullet(self.bullet_x, self.bullet_y)
            bill_bullet.speed_x = self.bullet_speed_x
            bill_bullet.speed_y = self.bullet_speed_y
            Variable.all_sprites.add(bill_bullet)
            self.bullet_time = 0
        else:
            self.bullet_time += 1

子弹发射成功! 

三、添加敌人:

1、敌人图片载入

敌人就简单一点吧,只选择了一个图片形象的,PS处理完,一共7张图片,6个行走运动的,1个射击状态的,这里让敌人从游戏窗口外面开始落下来,坠落状态的图片就省去了。

设定一个敌人图片列表,同样需要载入图片并按照PLAYER_SCALE比例放大。

再设定一些变量,来控制敌人的产生时间间隔以及发射子弹间隔等。

import os.path
import random

import pygame

from Config import Constant, Variable


class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.enemy_list = []
        for i in range(7):
            image = pygame.image.load(os.path.join('image', 'enemy', 'enemy' + str(i + 1) + '.png'))
            rect = image.get_rect()
            image = pygame.transform.scale(
                image, (rect.width * Constant.PLAYER_SCALE, rect.height * Constant.PLAYER_SCALE))
            image = pygame.transform.flip(image, True, False)
            self.enemy_list.append(image)
        self.order = 0
        self.shooting = False
        if self.shooting:
            self.image = self.enemy_list[6]
        else:
            self.image = self.enemy_list[self.order]
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(Constant.WIDTH, Constant.WIDTH * 5)
        self.rect.y = 100

        self.counter = 0
        self.speed = 2
        self.floor = 0

        self.last_time = 0
        self.now_time = 0
2、敌人的产生方法

定义一个敌人的组,用来判定碰撞及控制敌人数量。

0-100之间取一个随机数,等与50时产生一个敌人,分别加入敌人组和所有精灵组。

def new_enemy():
    if Variable.step == 2 and len(Variable.enemy_sprites) <= 3:
        i = random.randint(0, 100)
        if i == 50:
            enemy = Enemy()
            Variable.all_sprites.add(enemy)
            Variable.enemy_sprites.add(enemy)
3、移动及射击

先写个索引循环方法,控制order,每完成一次移动图片,计数器counter+1,到5时射击一次。也就是6张移动图片轮回5次就射击一次。

    def order_loop(self):
        self.now_time = pygame.time.get_ticks()
        if self.now_time - self.last_time >= 150:
            self.order += 1
            if self.order > 6:
                self.counter += 1
                self.order = 0
                if self.counter >= 5:
                    self.shooting = True
            self.last_time = self.now_time

 再定义一个射击方法,子弹的位置和移动速度

    def shoot(self):
        enemy_bullet = ContraBullet.Bullet(self.rect.left, self.rect.y + 7.5 * Constant.PLAYER_SCALE)
        enemy_bullet.speed_x = -10
        Variable.all_sprites.add(enemy_bullet)
        self.shooting = False
        self.counter = 0

 更新update,实现移动、图片改变及射击。同样也是让敌人一直处于下落状态,碰到地面碰撞体就停止下落,并向左移动。这里写的比较简单,没有考虑敌人的跳跃问题。

    def update(self):
        if self.shooting:
            self.image = self.enemy_list[6]
        else:
            self.image = self.enemy_list[self.order]
        self.rect.y += Constant.GRAVITY * 10
        self.order_loop()

        if self.shooting:
            self.shoot()

        collider_ground = pygame.sprite.groupcollide(Variable.enemy_sprites, Variable.collider, False, False)
        for e, l in collider_ground.items():
            self.floor = l[0].rect.top
            e.rect.bottom = self.floor
            e.rect.x -= self.speed

四、全部文件:

剩下的就是各种碰撞检测和关头了,这里就不再写啦。把所有文件都完整贴一遍。

1、Contra.py主循环文件

感觉都挺长时间没改动这个文件了,就增加了一个ContraEnemy.new_enemy()。

# Contra.py
import sys
import pygame

import ContraBill
import ContraMap
import ContraEnemy
from Config import Constant, Variable


def control():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Variable.game_start = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                Variable.step = 1


class Main:
    def __init__(self):
        pygame.init()
        self.game_window = pygame.display.set_mode((Constant.WIDTH, Constant.HEIGHT))
        self.clock = pygame.time.Clock()

    def game_loop(self):
        while Variable.game_start:
            control()
            if Variable.stage == 1:
                ContraMap.new_stage()
                ContraBill.new_player()
                ContraEnemy.new_enemy()

            if Variable.stage == 2:
                pass

            if Variable.stage == 3:
                pass

            Variable.all_sprites.draw(self.game_window)
            Variable.all_sprites.update()

            self.clock.tick(Constant.FPS)
            pygame.display.set_caption(f'魂斗罗  1.0    {self.clock.get_fps():.2f}')
            pygame.display.update()

        pygame.quit()
        sys.exit()


if __name__ == '__main__':
    main = Main()
    main.game_loop()
    
2、ContraMap.py第一关背景地图及地面碰撞体文件

别的关的背景地图也是按照这个方式,再单独测算每个台阶的位置和长度,一条条的把地面碰撞体给绘制出来就可以了。

# ContraMap.py
import os
import pygame

from Config import Constant, Variable


class StageMap(pygame.sprite.Sprite):
    def __init__(self, order):
        pygame.sprite.Sprite.__init__(self)
        self.image_list = []
        self.order = order
        for i in range(1, 9):
            image = pygame.image.load(os.path.join('image', 'map', 'stage' + str(i) + '.png'))
            rect = image.get_rect()
            image = pygame.transform.scale(image, (rect.width * Constant.MAP_SCALE, rect.height * Constant.MAP_SCALE))
            self.image_list.append(image)
        self.image = self.image_list[self.order]
        self.rect = self.image.get_rect()
        self.rect.x = 0
        self.rect.y = 0
        self.speed = 0

    def update(self):
        if self.order == 2:
            print('纵向地图')
        else:
            if Variable.step == 0 and self.rect.x >= -Constant.WIDTH:
                self.rect.x -= 10
            if Variable.step == 1 and self.rect.x > -Constant.WIDTH * 2:
                self.rect.x -= 10
                if self.rect.x == -Constant.WIDTH * 2:
                    Variable.step = 2
                    Variable.all_sprites.add(Variable.collider)


def new_stage():
    if Variable.map_switch:
        stage_map = StageMap(Variable.stage - 1)
        Variable.all_sprites.add(stage_map)
        Variable.map_storage.add(stage_map)
        Variable.map_switch = False


class CollideGround(pygame.sprite.Sprite):
    def __init__(self, length, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((length * Constant.MAP_SCALE, 3))
        self.image.fill((255, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.x = x * Constant.MAP_SCALE - Constant.WIDTH * 2
        self.rect.y = y * Constant.MAP_SCALE


Variable.collider83.add(
    CollideGround(511, 2176, 83),
    CollideGround(161, 2847, 83),
    CollideGround(64, 3296, 83)
)

Variable.collider115.add(
    CollideGround(736, 832, 115),
    CollideGround(128, 1568, 115),
    CollideGround(160, 1695, 115),
    CollideGround(128, 1856, 115),
    CollideGround(256, 1984, 115),
    CollideGround(224, 2656, 115),
    CollideGround(65, 3040, 115),
    CollideGround(64, 3264, 115),
    CollideGround(64, 3392, 115),
    CollideGround(128, 3808, 115)
)

Variable.collider146.add(
    CollideGround(97, 959, 146),
    CollideGround(66, 1215, 146),
    CollideGround(225, 2400, 146),
    CollideGround(96, 2976, 146),
    CollideGround(64, 3136, 146),
    CollideGround(160, 3424, 146),
    CollideGround(64, 3744, 146),
    CollideGround(32, 3936, 146)
)

Variable.collider163.add(
    CollideGround(95, 1440, 163),
    CollideGround(64, 2304, 163),
    CollideGround(32, 2912, 163),
    CollideGround(32, 2328, 163),
    CollideGround(32, 3328, 163),
    CollideGround(97, 3840, 163)
)

Variable.collider178.add(
    CollideGround(32, 1055, 178),
    CollideGround(32, 1151, 178),
    CollideGround(65, 2720, 178),
    CollideGround(64, 2816, 178),
    CollideGround(96, 3168, 178),
    CollideGround(63, 3648, 178),
    CollideGround(32, 3969, 178)
)

Variable.collider211.add(
    CollideGround(63, 1088, 211),
    CollideGround(63, 1407, 211),
    CollideGround(97, 2208, 211),
    CollideGround(192, 2528, 211),
    CollideGround(33, 3135, 211),
    CollideGround(33, 3295, 211),
    CollideGround(97, 3520, 211),
    CollideGround(242, 3807, 211)
)

Variable.collider.add(Variable.collider83, Variable.collider115, Variable.collider146, Variable.collider163,
                      Variable.collider178, Variable.collider211, Variable.collider231)
3、ContraBill.py主角玩家文件

主角Bill向下跳跃,后来想了想,是不是可以考虑写一个矮点的碰撞体紧随Bill的脚下,然后由这个碰撞体来检测是否和地面碰撞体碰撞,这样就不用等Bill的top超过地面碰撞体就可以把它加回来了。而且在往上跳时,也不会因为两层地面距离太近直接跳到上面层了,感觉体验感应该会好些。

# ContraBill.py
import os
import pygame

import ContraBullet

from Config import Constant, Variable


class Bill(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image_dict = {
            'be_hit': [],
            'down': [],
            'jump': [],
            'oblique_down': [],
            'oblique_up': [],
            'run': [],
            'shoot': [],
            'stand': [],
            'up': []
        }
        for i in range(6):
            for key in self.image_dict:
                image = pygame.image.load(os.path.join('image', 'bill', str(key), str(key) + str(i + 1) + '.png'))
                rect = image.get_rect()
                image_scale = pygame.transform.scale(
                    image, (rect.width * Constant.PLAYER_SCALE, rect.height * Constant.PLAYER_SCALE))
                self.image_dict[key].append(image_scale)

            self.image_order = 0
            self.image_type = 'stand'

            self.image = self.image_dict[self.image_type][self.image_order]
            self.rect = self.image.get_rect()
            self.rect.x = 100
            self.rect.y = 100
            self.direction = 'right'

            self.now_time = 0
            self.last_time = 0

            self.falling = True
            self.jumping = False
            self.moving = False
            self.floor = 0

            self.bullet_time = 0
            self.bullet_speed_x = 0
            self.bullet_speed_y = 0
            self.bullet_x = self.rect.right
            self.bullet_y = self.rect.y + 11.5 * Constant.PLAYER_SCALE

    def update(self):
        self.image = self.image_dict[self.image_type][self.image_order]
        if self.direction == 'left':
            self.image = pygame.transform.flip(self.image, True, False)

        if self.rect.top >= self.floor:
            for i in Variable.sprite_storage:
                Variable.collider.add(i)
                Variable.sprite_storage.remove(i)

        key_pressed = pygame.key.get_pressed()

        if self.falling:
            self.fall()

        if self.jumping:
            self.jump()

        if self.moving:
            self.move()

        self.order_loop()

        collider_ground = pygame.sprite.groupcollide(Variable.player_sprites, Variable.collider, False, False)
        for p, l in collider_ground.items():
            self.floor = l[0].rect.top
            p.rect.bottom = self.floor

            if key_pressed[pygame.K_k]:
                self.shoot()

            if key_pressed[pygame.K_d]:
                self.direction = 'right'
                self.moving = True
                if key_pressed[pygame.K_w]:
                    self.image_type = 'oblique_up'
                elif key_pressed[pygame.K_s]:
                    self.image_type = 'oblique_down'
                elif key_pressed[pygame.K_j]:
                    self.image_type = 'jump'
                    self.jumping = True
                    self.falling = False
                else:
                    self.image_type = 'run'
            elif key_pressed[pygame.K_a]:
                self.direction = 'left'
                self.moving = True
                if key_pressed[pygame.K_w]:
                    self.image_type = 'oblique_up'
                elif key_pressed[pygame.K_s]:
                    self.image_type = 'oblique_down'
                elif key_pressed[pygame.K_j]:
                    self.image_type = 'jump'
                    self.jumping = True
                    self.falling = False
                else:
                    self.image_type = 'run'

            elif key_pressed[pygame.K_w]:
                self.image_type = 'up'
                self.moving = False

            elif key_pressed[pygame.K_s]:
                self.image_type = 'down'
                self.moving = False

            else:
                self.image_type = 'stand'
                self.moving = False

            if key_pressed[pygame.K_s] and key_pressed[pygame.K_j]:
                self.image_type = 'oblique_down'
                Variable.collider.remove(l[0])
                Variable.sprite_storage.add(l[0])

    def shoot(self):
        if self.direction == 'right':
            self.bullet_speed_x = Constant.BULLET_SPEED
            self.bullet_x = self.rect.right
        elif self.direction == 'left':
            self.bullet_speed_x = -Constant.BULLET_SPEED
            self.bullet_x = self.rect.left

        if self.image_type == 'stand':
            self.bullet_y = self.rect.y + 11.5 * Constant.PLAYER_SCALE
            self.bullet_speed_y = 0
        elif self.image_type == 'down':
            self.bullet_y = self.rect.y + 24 * Constant.PLAYER_SCALE
            self.bullet_speed_y = 0
        elif self.image_type == 'up':
            self.bullet_y = self.rect.y
            self.bullet_speed_x = 0
            self.bullet_speed_y = -Constant.BULLET_SPEED
        elif self.image_type == 'oblique_down':
            self.bullet_y = self.rect.y + 20.5 * Constant.PLAYER_SCALE
            self.bullet_speed_y = Constant.BULLET_SPEED
        elif self.image_type == 'oblique_up':
            self.bullet_y = self.rect.y
            self.bullet_speed_y = -Constant.BULLET_SPEED

        if self.bullet_time >= 30:
            bill_bullet = ContraBullet.Bullet(self.bullet_x, self.bullet_y)
            bill_bullet.speed_x = self.bullet_speed_x
            bill_bullet.speed_y = self.bullet_speed_y
            Variable.all_sprites.add(bill_bullet)
            self.bullet_time = 0
        else:
            self.bullet_time += 1

    def order_loop(self):
        self.now_time = pygame.time.get_ticks()
        if self.now_time - self.last_time >= 100:
            self.image_order += 1
            if self.image_order > 5:
                self.image_order = 0
            self.last_time = self.now_time

    def move(self):
        if self.direction == 'right' and self.rect.right <= Constant.WIDTH - 80 * Constant.MAP_SCALE:
            self.rect.x += Constant.SPEED_X
            if self.rect.centerx >= Constant.WIDTH / 2:
                x = self.rect.centerx - Constant.WIDTH / 2
                for j in Variable.map_storage:
                    if j.rect.right >= Constant.WIDTH:
                        for i in Variable.all_sprites:
                            i.rect.x -= x

        elif self.direction == 'left' and self.rect.left >= 40 * Constant.MAP_SCALE:
            self.rect.x -= Constant.SPEED_X
            if self.rect.centerx <= Constant.WIDTH / 2:
                x = Constant.WIDTH / 2 - self.rect.centerx
                for j in Variable.map_storage:
                    if j.rect.left <= -Constant.WIDTH * 2:
                        for i in Variable.all_sprites:
                            i.rect.x += x

    def jump(self):
        Constant.SPEED_Y += Constant.GRAVITY
        self.rect.y += Constant.SPEED_Y
        if Constant.SPEED_Y == 0:
            self.jumping = False
            self.falling = True
            Constant.SPEED_Y = -10

    def fall(self):
        self.rect.y += Constant.GRAVITY * 10


def new_player():
    if Variable.step == 2 and Variable.player_switch:
        bill = Bill()
        Variable.all_sprites.add(bill)
        Variable.player_sprites.add(bill)
        Variable.player_switch = False
4、ContraEnemy.py

敌人这里写的太简单了,连出游戏窗口的判定都没写!我记得正常游戏里面还有一些在墙上的炮台啥的,还有随机掉落的各种buff以及关头。

import os.path
import random

import pygame

import ContraBullet
from Config import Constant, Variable


class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.enemy_list = []
        for i in range(7):
            image = pygame.image.load(os.path.join('image', 'enemy', 'enemy' + str(i + 1) + '.png'))
            rect = image.get_rect()
            image = pygame.transform.scale(
                image, (rect.width * Constant.PLAYER_SCALE, rect.height * Constant.PLAYER_SCALE))
            image = pygame.transform.flip(image, True, False)
            self.enemy_list.append(image)
        self.order = 0
        self.shooting = False
        if self.shooting:
            self.image = self.enemy_list[6]
        else:
            self.image = self.enemy_list[self.order]
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(Constant.WIDTH, Constant.WIDTH * 5)
        self.rect.y = 100

        self.counter = 0
        self.speed = 2
        self.floor = 0

        self.last_time = 0
        self.now_time = 0

    def update(self):
        if self.shooting:
            self.image = self.enemy_list[6]
        else:
            self.image = self.enemy_list[self.order]
        self.rect.y += Constant.GRAVITY * 10
        self.order_loop()

        if self.shooting:
            self.shoot()

        collider_ground = pygame.sprite.groupcollide(Variable.enemy_sprites, Variable.collider, False, False)
        for e, l in collider_ground.items():
            self.floor = l[0].rect.top
            e.rect.bottom = self.floor
            e.rect.x -= self.speed

    def order_loop(self):
        self.now_time = pygame.time.get_ticks()
        if self.now_time - self.last_time >= 150:
            self.order += 1
            if self.order > 6:
                self.counter += 1
                self.order = 0
                if self.counter >= 5:
                    self.shooting = True
            self.last_time = self.now_time

    def shoot(self):
        enemy_bullet = ContraBullet.Bullet(self.rect.left, self.rect.y + 7.5 * Constant.PLAYER_SCALE)
        enemy_bullet.speed_x = -10
        Variable.all_sprites.add(enemy_bullet)
        self.shooting = False
        self.counter = 0


def new_enemy():
    if Variable.step == 2 and len(Variable.enemy_sprites) <= 3:
        i = random.randint(0, 100)
        if i == 50:
            enemy = Enemy()
            Variable.all_sprites.add(enemy)
            Variable.enemy_sprites.add(enemy)
5、ContraBullet.py

子弹这里没有区分双方的子弹,同样也没写出游戏窗口后kill。

import os.path

import pygame

from Config import Constant


class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.bullet_list = []
        self.bullet_number = 0
        for i in range(6):
            image = pygame.image.load(os.path.join('image', 'bullet', 'bullet' + str(i + 1) + '.png'))
            rect = image.get_rect()
            image = pygame.transform.scale(
                image, (rect.width * Constant.PLAYER_SCALE, rect.height * Constant.PLAYER_SCALE))
            self.bullet_list.append(image)
        self.image = self.bullet_list[self.bullet_number]
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y
        self.speed_x = 0
        self.speed_y = 0

        self.counter = 0

    def update(self):
        if self.counter >= 30:
            self.bullet_number += 1
            if self.bullet_number > 5:
                self.bullet_number = 0
        else:
            self.counter += 1
        self.image = self.bullet_list[self.bullet_number]
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y
6、Config.py

把游戏的全局常量、变量单独写出来,感觉在调用时还是挺方便的。

# Config.py
import pygame


class Constant:
    WIDTH = 1200
    HEIGHT = 720
    FPS = 60

    MAP_SCALE = 3
    PLAYER_SCALE = 2.5

    SPEED_X = 3
    SPEED_Y = -10
    GRAVITY = 0.5

    BULLET_SPEED = 8


class Variable:
    all_sprites = pygame.sprite.Group()
    player_sprites = pygame.sprite.Group()
    enemy_sprites = pygame.sprite.Group()

    collider = pygame.sprite.Group()
    collider83 = pygame.sprite.Group()
    collider115 = pygame.sprite.Group()
    collider146 = pygame.sprite.Group()
    collider163 = pygame.sprite.Group()
    collider178 = pygame.sprite.Group()
    collider211 = pygame.sprite.Group()
    collider231 = pygame.sprite.Group()

    sprite_storage = pygame.sprite.Group()
    map_storage = pygame.sprite.Group()

    game_start = True

    map_switch = True
    player_switch = True

    enemy_counter = 0

    stage = 1
    step = 0


网站公告

今日签到

点亮在社区的每一天
去签到