前言

接上篇文章继续解绍arcade游戏编程的基本知识。

带炮台的坦克

1、可旋转炮台的坦克实现

1.1坦克由两部分组成

在这里插入图片描述

1.2 组装坦克
    # 组装坦克
    self.tank = arcade.Sprite(TANK_BODY,image_x=0,image_y=0,image_width=132,image_height=243,scale=1)
    self.tank.position = SCREEN_MIDDLE

    self.barrel = RotatingSprite(TANK_BARREL, image_x=150,image_y=43,image_width=86,image_height=200,scale=1)
    self.barrel.position =\
        SCREEN_MIDDLE[0], SCREEN_MIDDLE[1] - TANK_panli
1.3 效果图

在这里插入图片描述
炮台随鼠标可以转动
在这里插入图片描述
围绕点的精灵旋转,带炮台的坦克,游戏通常包括朝向目标旋转的元素。常见的例如,车辆和塔楼上的炮塔。在2D游戏中,这些旋转部件通常被实现为移动的精灵相对于他们所依附的任何东西。
这有一个问题:你必须围绕它们的附着点而不是精灵的中心。否则旋转看起来不对!为了说明区别,本例使用了一个可控制的播放器有一个跟在鼠标后面的坦克。
1.正确,枪管后部紧靠坦克中心
2.错误的是,围绕枪管中心,将其固定在坦克上

1.4 代码实现
import arcade
import math


TANK_SPEED_PIXELS = 64  # 坦克每秒移动多少像素
TANK_TURN_SPEED_DEGREES = 70  # 坦克车身转动的速度


# 这是炮台精灵旋转中心位置与坦克中心的偏离差。
# 我们使用它来确保炮台的后部位于坦克的中间
TANK_panli = 40


SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_MIDDLE = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)


SCREEN_TITLE = "上下旋转的坦克"


# These paths are built-in resources included with arcade
TANK_BODY = "images/坦克1.png"
TANK_BARREL = "images/坦克1.png"


class RotatingSprite(arcade.Sprite):
    """
    可以围绕一个点旋转的精灵子类。这个版本的类总是改变精灵的角度。
    其他游戏可能不会旋转精灵。例如,移动平台成型机中的平台不会旋转。
    """
    def rotate_around_point(self, point: arcade.Point, degrees: float):

        # 使精灵在其位置移动时旋转
        self.angle += degrees

        # 沿着以传递点为中心的圆移动精灵
        self.position = arcade.rotate_point(
            self.center_x, self.center_y,
            point[0], point[1], degrees)


class ExampleWindow(arcade.Window):

    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

        # 设置背景色
        self.background_color = arcade.csscolor.SEA_GREEN

        # 组装坦克
        self.tank = arcade.Sprite(TANK_BODY,image_x=0,image_y=0,image_width=132,image_height=243,scale=1)
        self.tank.position = SCREEN_MIDDLE

        self.barrel = RotatingSprite(TANK_BARREL, image_x=150,image_y=43,image_width=86,image_height=200,scale=1)
        self.barrel.position =\
            SCREEN_MIDDLE[0], SCREEN_MIDDLE[1] - TANK_panli

        self.tank_direction = 0.0  # # 前进和后退油门
        self.tank_turning = 0.0  # 向左或向右转的力度

        self.mouse_pos = 0, 0

        self.tank_sprite_list = arcade.SpriteList()
        self.tank_sprite_list.extend([self.tank, self.barrel])

        self.control_text = arcade.Text(
            "WASD 键移动坦克, 鼠标移动炮台指向",
            SCREEN_MIDDLE[0], 15,
            anchor_x='center')

    def on_draw(self):
        self.clear()
        self.tank_sprite_list.draw()

        self.control_text.draw()


    def on_update(self, delta_time: float):
        self.move_tank(delta_time)

    def move_tank(self, delta_time):

        # 在不改变位置的情况下将罐体旋转到位,我们将在更新整个坦克的x轴和y轴后旋转枪管
        self.tank.angle += TANK_TURN_SPEED_DEGREES\
            * self.tank_turning * delta_time

        #计算应向前或向后移动多少
        move_magnitude = self.tank_direction * TANK_SPEED_PIXELS * delta_time
        x_dir = math.cos(self.tank.radians - math.pi / 2) * move_magnitude
        y_dir = math.sin(self.tank.radians - math.pi / 2) * move_magnitude

        # 移动坦克底盘
        self.tank.position =\
            self.tank.center_x + x_dir,\
            self.tank.center_y + y_dir

        #移动炮台
        self.barrel.position =\
            self.barrel.center_x + x_dir,\
            self.barrel.center_y + y_dir

        # 计算坦克与鼠标的角度
        mouse_angle = arcade.get_angle_degrees(
            self.tank.center_y, self.tank.center_x,
            self.mouse_pos[1], self.mouse_pos[0])

        # 补偿原始图片的垂直方向,如果图片朝右,则可以跳过此操作
        mouse_angle += 90

        angle_change = mouse_angle - self.barrel.angle

        self.barrel.rotate_around_point(self.tank.position, angle_change)

    def on_key_press(self, symbol: int, modifiers: int):
        if symbol == arcade.key.W:
            self.tank_direction += 1
        elif symbol == arcade.key.S:
            self.tank_direction -= 1
        elif symbol == arcade.key.A:
            self.tank_turning += 1
        elif symbol == arcade.key.D:
            self.tank_turning -= 1
        elif symbol == arcade.key.P:
            pass

    def on_key_release(self, symbol: int, modifiers: int):
        if symbol == arcade.key.W:
            self.tank_direction -= 1
        elif symbol == arcade.key.S:
            self.tank_direction += 1
        elif symbol == arcade.key.A:
            self.tank_turning -= 1
        elif symbol == arcade.key.D:
            self.tank_turning += 1

    def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
        self.mouse_pos = x, y

def main():
    window = ExampleWindow()
    window.run()


if __name__ == '__main__':
    main()

2、以上示例改为类,便于集成游戏中

2.1 常量 存于seting文件中

坦克属性
TANK_body = “images/坦克1.png”
TANK_barrel = “images/坦克1.png”
TANK_pos = (SCREEN_width// 2, SCREEN_height // 2)
TANK_pianli = 15
TANK_speed_pixels = 64 #坦克每秒移动多少像素
TANK_turn_speed_degrees = 70 #坦克车身转动的速度

2.2 坦克类

from arcadegame.seting import *
import arcade
import math

class RotatingSprite(arcade.Sprite):
“”" 可以围绕一个点旋转的精灵子类。 “”"
def rotate_around_point(self, point: arcade.Point, degrees: float):
# 使精灵在其位置移动时旋转
self.angle += degrees

    # 沿着以传递点为中心的圆移动精灵
    self.position = arcade.rotate_point(
        self.center_x, self.center_y,
        point[0], point[1], degrees)

class Tanke(RotatingSprite):
def init(self):
super().init()
# The tank and barrel sprite
self.tank = arcade.Sprite(TANK_body,image_x=0,image_y=0,image_width=132,image_height=243,scale=0.5)
self.tank.position = TANK_pos

    self.barrel = RotatingSprite(TANK_barrel, image_x=150,image_y=0,image_width=89,image_height=243,scale=0.5)
    self.barrel.position =\
        TANK_pos[0], TANK_pos[1] - TANK_pianli

def move_tank(self, delta_time,tank_turning,tank_direction,mouse_pos):
    # 在不改变位置的情况下将罐体旋转到位,我们将在更新整个坦克的x轴和y轴后旋转枪管
    self.tank.angle += TANK_turn_speed_degrees\
        * tank_turning * delta_time

    #计算应向前或向后移动多少
    move_magnitude = tank_direction * TANK_speed_pixels * delta_time
    x_dir = math.cos(self.tank.radians - math.pi / 2) * move_magnitude
    y_dir = math.sin(self.tank.radians - math.pi / 2) * move_magnitude

    # 移动坦克底盘
    self.tank.position =\
        self.tank.center_x + x_dir,\
        self.tank.center_y + y_dir

    #移动炮台
    self.barrel.position =self.barrel.center_x + x_dir, self.barrel.center_y + y_dir

    # 计算坦克与鼠标的角度
    mouse_angle = arcade.get_angle_degrees(
        self.tank.center_y, self.tank.center_x,
        mouse_pos[1], mouse_pos[0])

    # 补偿炮台的垂直方向如果朝右,则可以跳过此操作
    mouse_angle += 90

    angle_change = mouse_angle - self.barrel.angle

    self.barrel.rotate_around_point(self.tank.position, angle_change)
2.3 Tank类的使用效果图

在这里插入图片描述

2.4 代码实现
"""
围绕点的精灵旋转,带坦克
游戏通常包括朝向目标旋转的元素。常见的例如,车辆和塔楼上的炮塔。在2D游戏中,
这些旋转部件通常被实现为移动的精灵相对于他们所依附的任何东西。
这有一个问题:你必须围绕它们的附着点而不是精灵的中心。否则旋转看起来不对!
为了说明区别,本例使用了一个可控制的播放器
有一个跟在鼠标后面的坦克。您可以按P切换
在两种旋转的方式之间:
1.正确,枪管后部紧靠坦克中心
2.错误的是,围绕枪管中心,将其固定在坦克上
"""

from arcadegame.tank import *

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_MIDDLE = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)

SCREEN_TITLE = "上下旋转的坦克"


class ExampleWindow(arcade.Window):

    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

        # Set Background to be green
        self.background_color = arcade.csscolor.SEA_GREEN

        self.tk = None
        self.tank_direction = 0.0  # # 前进和后退油门
        self.tank_turning = 0.0  # 向左或向右转的力度
        self.mouse_pos=0,0
        self.scene = None
        self.control_text = arcade.Text(
            "WASD 键移动坦克, 鼠标移动炮台指向",
            SCREEN_MIDDLE[0], 15,
            anchor_x='center')

    def setup(self):
        # 坦克
        self.tk = Tanke()
        # self.tank_sprite_list = arcade.SpriteList()
        # self.tank_sprite_list.extend([self.tk.tank, self.tk.barrel])
        # 初始化场景
        map_name = f"地图\家2.json"  # jia{self.level}.json"
        # 层属性
        layer_options = {
            LAYER_platforms: {
                "use_spatial_hash": True,
            },
            LAYER_tree: {
                "use_spatial_hash": False,
            },
            'fz': {
                "use_spatial_hash": True,
            },
            # 'tizi': {
            #     "use_spatial_hash": True,
            # },
            'Enemies': {
                "use_spatial_hash": True,
            },
        }

        # 读地图文件
        self.tile_map = arcade.load_tilemap(map_name, TILE_Scaling, layer_options)
         # 使用我们的TileMap初始化场景,这将自动添加所有层,以正确的顺序在场景中显示为SpriteList。
        self.scene = arcade.Scene.from_tilemap(self.tile_map)

        self.scene.add_sprite("tanke", self.tk.tank)
        self.scene.add_sprite("tanke", self.tk.barrel)


    def on_draw(self):
        self.clear()
        # self.tank_sprite_list.draw()
        self.scene.draw()
        self.control_text.draw()

    def on_update(self, delta_time: float):
        self.tk.move_tank(delta_time,self.tank_turning,self.tank_direction,self.mouse_pos)

    def on_key_press(self, symbol: int, modifiers: int):
        if symbol == arcade.key.W:
            self.tank_direction += 1
        elif symbol == arcade.key.S:
            self.tank_direction -= 1
        elif symbol == arcade.key.A:
            self.tank_turning += 1
        elif symbol == arcade.key.D:
            self.tank_turning -= 1
        elif symbol == arcade.key.P:
            pass

    def on_key_release(self, symbol: int, modifiers: int):
        if symbol == arcade.key.W:
            self.tank_direction -= 1
        elif symbol == arcade.key.S:
            self.tank_direction += 1
        elif symbol == arcade.key.A:
            self.tank_turning -= 1
        elif symbol == arcade.key.D:
            self.tank_turning += 1

    def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
        self.mouse_pos = x, y

def main():
    window = ExampleWindow()
    window.setup()
    window.run()


if __name__ == '__main__':
    main()

源码获取

关注博主后,私聊博主免费获取
需要技术指导,育娃新思考,企业软件合作等更多服务请联系博主

今天是以此模板持续更新此育儿专栏的第 17/50次。
可以关注我,点赞我、评论我、收藏我啦。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐