wxPython和pycairo练习记录8

基于Java+SpringBoot+Vue的中小企业设备管理系统的设计与开发(源码+lw+部署文档+讲解等) 💗博主介绍:✌新人博主,工作经验两年+、专注于Java、小程序技术领域和毕业项目实战✌💗🌟文末获取源码+数据库🌟 阅读详情

wxPython和pycairo练习记录8

经过障碍物的编写,发现需要停止前进并重新梳理一下。
1.新的检测项加入时,已经在向混乱发展。是时候用上封装变化的编程工具,用策略模式或状态模式重写。
2.显示,在基类引入层级和优先级,按层级绘制所有 Sprite 对象。
3.范围的碰撞和本身的碰撞的作用顺序。还有像已经实现的碉堡,只有一个外层碰撞区域,如果要多加几层呢,最外层出现坦克,射速为1,再近一层射速为5,最近一层射速达到20。
4.计时,因为 wxPython 的时间事件 wx.EVT_TIMER,产生了错误的认识,觉得必须要靠 FPS 来计时。
5.pycairo 移动原点的作用,原点在左上角,移动原点相当于移动了可视区,旋转是以原点作圆心旋转,翻转是以过原点的 x 轴 和 y 轴为对称轴。
6.相邻障碍物远程作用叠加在一起,需要排除其他障碍物所在区域。
7.把全局类独立到一个模块文件,配置项、素材。
8.垃圾绘制对象的清除,destroyed 属性为 True 的。超出边界的子弹,需要设置 destroyed 。
9.键盘控制方向处理方式。

8.1 层级管理

关于显示层级,如果能碰撞到,说明处在同一层;如果不能碰到,则要么在上层,要么在下层。就像科幻里,高维生物与低维生物,视觉上是在同一个地方,但属于不同维度,无法被攻击到。

绘制是后绘制先显示,栈是后进先出。用栈管理层级?不方便调整显示顺序啊。层级,还是树形结构比较符合。分层,把屏幕作为根节点,创建不同的层作为子节点,子节点组成父节点,例如坦克作为父节点,它的组成部件作为子节点,那么就只需要绘制组成部件。优先级,先绘制为优先,从上向下,从左向右,优先级递减。定好规则后,只需要进行先根序遍历,逐一绘制就行了。

Sprite 对象默认都加入全局变量指定的 layer 层。
在这里插入图片描述

引入层级管理,上下图层效果演示。看起来是跟之前差不多的,但逻辑更清楚一些。
在这里插入图片描述

# -*- coding: utf-8 -*-
# layer.py


class Layer:
    def __init__(self, data="Screen"):
        self.data = data
        self.parent = None
        self.left = None
        self.right = None
        self.firstChild = None
        self.lastChild = None

    def Append(self, tree):
        # 向最右添加子节点
        tree.parent = self

        if self.lastChild:
            self.lastChild.right = tree
            tree.left = self.lastChild
            self.lastChild = tree
        else:
            self.lastChild = self.firstChild = tree

        return self

    def PreAppend(self, tree):
        # 向最左添加子节点
        tree.parent = self

        if self.firstChild:
            self.firstChild.left = tree
            tree.right = self.firstChild
            self.firstChild = tree
        else:
            self.firstChild = self.lastChild = tree

        return self

    def Insert(self, tree, left):
        # 在指定节点右插入节点
        tree.parent = self
        tree.left = left
        tree.right = left.right

        if left is self.lastChild:
            self.lastChild = tree
        else:
            left.right.left = tree

        left.right = tree

        return self

    def PreInsert(self, tree, right):
        # 在指定节点左插入节点
        tree.parent = self
        tree.right = right
        tree.left = right.left

        if right is self.firstChild:
            self.firstChild = tree
        else:
            right.left.right = tree

        right.left = tree

        return self

    def Delete(self, tree):
        # 删除指定节点
        if tree.parent is not self:
            return

        if tree.left:
            tree.left.right = tree.right
            if tree is self.lastChild:
                self.lastChild = tree.left

        if tree.right:
            tree.right.left = tree.left
            if tree is self.firstChild:
                self.firstChild = tree.right

        if tree is self.firstChild and tree is self.lastChild:
            self.firstChild = None
            self.lastChild = None

        tree.parent = None
        tree.left = None
        tree.right = None

        return self

    def Exchange(self, tree1, tree2):
        # 交换位置
        if (tree1.parent is not self) or (tree2.parent is not self) or (not self.firstChild) or (tree1 is tree2):
            return
        # 逻辑有点混乱,直接暴力交换
        tmp = Layer("tmp")
        self.Insert(tmp, tree1)
        self.Delete(tree1)
        self.Insert(tree1, tree2)
        self.Delete(tree2)
        self.Insert(tree2, tmp)
        self.Delete(tmp)
        del tmp

    def Clear(self):
        self.parent = None
        self.left = None
        self.right = None
        self.firstChild = None
        self.lastChild = None
        return self

    def SetParent(self, parent):
        # 设置父节点,移动,没解决parent首尾相连构成循环的问题
        self.parent.Delete(self)
        parent.Append(self)
        return self

    def __Draw(self, node, ctx):
        # 绘制
        if hasattr(node.data, "GetSurface"):
            sprite = node.data
            ctx.set_source_surface(sprite.GetSurface(), sprite.GetX(), sprite.GetY())
            ctx.paint()

    def Display(self, ctx):
        # 显示全部 Sprite 对象
        if (not self.parent) and self.data == "Screen" and self.firstChild:
            self.Traverse(self, self.__Draw, ctx)

    @classmethod
    def Traverse(cls, root, func, *args):
        # 遍历子节点
        node = root.firstChild
        while node:
            # 只对没有子节点的节点执行操作
            if not node.firstChild:
                func(node, *args)
            cls.Traverse(node, func, *args)
            node = node.right

8.2 修改配置文件

主要是把素材和图层配置放到公共文件。

# -*- coding: utf-8 -*-
# const.py

import os
import glob
from cairo import ImageSurface
from layer import Layer


class cv:
    """
    常量,用类属性避免使用全局变量
    """
    # 刷新定时器 id
    TIMER_ID = 1
    # 刷新次数
    times = 0
    # 刷新间隔时间 毫秒, FPS (frames per second) 就是 1000 // SPEED
    SPEED = 10
    # 面板尺寸
    BOARD_WIDTH = 600
    BOARD_HEIGHT = 400

    # 全局图层,管理对象显示
    screen = Layer()
    layer0 = Layer("layer0") # 背景
    layer1 = Layer("layer1") # 水面、冰面、传送阵
    layer2 = Layer("layer2") # 坦克、子弹、碉堡、砖墙
    layer3 = Layer("layer3") # 草地
    layer4 = Layer("layer4") # 碉堡感应范围、障碍作用解除触发范围
    screen.Append(layer0).Append(layer1).Append(layer2).Append(layer3).Append(layer4)
    layer = layer2 # 默认图层

    # 素材文件路径
    img_path = "./resource/img/"
    # 图片直接加载为 pycairo 的 surface
    surfaces = {}
    for path in glob.glob(img_path + "*.png"):
        surfaceName = os.path.basename(path).split(".")[0]
        surfaces[surfaceName] = ImageSurface.create_from_png(path)

8.3 修改图像容器

引入图层属性,值为 Layer 对象,可通过它修改显示的层级。

# -*- coding: utf-8 -*-
# display.py

import wx
import cairo
from const import cv
from layer import Layer

class Sprite:
    def __init__(self, x, y, surface, parentLayer=cv.layer):
        self._x = x
        self._y = y
        self._destroyed = False # 销毁状态

        # 创建 surface 副本, 没找到简单一点的方法
        self._width = surface.get_width()
        self._height = surface.get_height()
        self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._width, self._height)
        ctx = cairo.Context(self._surface)
        ctx.set_source_surface(surface, 0, 0)
        ctx.paint()

        # 默认加入默认图层
        self._layer = Layer(self)
        parentLayer.Append(self._layer)

    def GetX(self):
        return self._x

    def SetX(self, x):
        self._x = x

    def GetY(self):
        return self._y

    def SetY(self, y):
        self._y = y

    def GetWidth(self):
        return self._width

    def SetWidth(self, width):
        self._width = width

    def GetHeight(self):
        return self._height

    def SetHeight(self, height):
        self._height = height

    def GetSurface(self):
        return self._surface

    def SetSurface(self, surface):
        self._surface = surface

    def GetRect(self):
        # 获取边框矩形对象,可用于碰撞检测等
        return wx.Rect(self._x, self._y, self._width, self._height)

    def IsDestroyed(self):
        return self._destroyed

    def Destroy(self):
        self._destroyed = True

    def GetLayer(self):
        return self._layer

    def SetParentLayer(self, layer):
        cv.layer.SetParent(layer)


class MovieClip(Sprite):
    def __init__(self, x, y, surface, rect, fps):
        super(MovieClip, self).__init__(x, y, surface)

        self._scenes = [] # 保存所有场景,每个场景保存当前场景所有帧
        self._frames = [] # 保存当前场景所有帧
        self._currentScene = 0 # 当前场景索引
        self._totalScenes = 0 # 总场景数
        self._currentFrame = 0 # 当前帧索引
        self._totalFrames = 0 # 当前场景总帧数
        self._isPlaying = False # 播放状态
        self._fps = fps  # 动画可能需要不同于主程序的FPS

        self.LoadFrames(rect)

    def LoadFrames(self, rect):
        # 按显示区域载入场景和帧
        x, y, width, height = rect

        # 每行表示一个场景scene,scene中每列表示一个frame
        try:
            for j in range(self._height // height):
                frames = []
                for i in range(self._width // width):
                    frame = self._surface.create_for_rectangle(x + width * i, y + height * j, width, height)
                    frames.append(frame)
                self._scenes.append(frames)
        except Exception as e:
            print(str(e))

        # 更新初始化变量
        self._surface = self._scenes[self._currentScene][self._currentFrame]
        self._totalScenes = len(self._scenes)
        self._totalFrames = len(self._scenes[self._currentScene])
        # create_for_rectangle 得到的 surface 获取到的宽高为0,这里直接使用矩形宽高
        self._width = width
        self._height = height

    def Play(self):
        self._isPlaying = True

    def Stop(self):
        self._isPlaying = False

    def PrevScene(self):
        self._currentScene -= 1
        self._isPlaying = False

    def NextScene(self):
        self._currentScene += 1
        self._isPlaying = False

    def PrevFrame(self):
        self._currentFrame -= 1
        self._isPlaying = False

    def NextFrame(self):
        self._currentFrame += 1
        self._isPlaying = False

    def GotoAndPlay(self, frame, scene):
        self._currentScene = scene
        self._currentFrame = frame
        self._isPlaying = True

    def GotoAndStop(self, frame, scene):
        self._currentScene = scene
        self._currentFrame = frame
        self._isPlaying = False

    def Update(self, times, speed):
        # 交给主程序执行,用于刷新 MovieClip 对象数据,主程序需要增加 times 用于记录主程序刷新次数
        # 主程序刷新速度要比 MovieClip 对象刷新快
        # print("刷新次数:", times, "当前场景索引:", self._currentScene,  "当前帧索引:", self._currentFrame)
        if times % (1000 // self._fps // speed) == 0 and self._isPlaying:
            self._currentFrame += 1

    def __setattr__(self, name, value):
        # 更新 _currentFrame 和 _currentScene 时,同时更新相应变量
        self.__dict__[name] = value

        if self.__dict__.get("_scenes"):
            if self.__dict__.get("_frames") and name == "_currentFrame":
                self._surface = self._frames[self._currentFrame % self._totalFrames]
            elif name == "_currentScene":
                self._frames = self._scenes[self._currentScene % self._totalScenes]
                self._totalFrames = len(self._frames)

8.4 一系列修改

显示部分暂时没问题,像碰撞检测之类还没有修改。

# -*- coding: utf-8 -*-
# main.py

import wx
from const import cv
from example import Board


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)
        Board(parent=self)
        self.Center() # 窗口在屏幕居中


def main():
    app = wx.App()
    frame = MyFrame(parent=None, title="cairotest", size=(cv.BOARD_WIDTH, cv.BOARD_HEIGHT))
    frame.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()

# -*- coding: utf-8 -*-
# board.py

import wx
import wx.lib.wxcairo
from const import cv


class Board(wx.Panel):
    def __init__(self, parent):
        super(Board, self).__init__(parent=parent, style=wx.WANTS_CHARS) # wx.WANTS_CHARS 可接收键盘事件
        self.SetDoubleBuffered(True) # 双缓冲,防止闪烁
        self.InitVariables()
        self.BindEvent()

    def InitVariables(self):
        # 初始化变量
        self.InitSceneObjects()
        # 设置定时器
        self.timer = wx.Timer(owner=self, id=cv.TIMER_ID)
        self.timer.Start(cv.SPEED)

    def InitSceneObjects(self):
        # 初始化场景中对象变量,如坦克实例、计分栏初始分数等
        self.sceneObjects = []

    def BindEvent(self):
        # 绑定事件
        self.Bind(wx.EVT_TIMER, self.OnTimer, id=cv.TIMER_ID)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)

    def OnTimer(self, e):
        # 处理更新事件,定时更新变量,刷新重绘
        cv.times += 1

        for so in self.sceneObjects:
            so.Update(cv.times, cv.SPEED)

        self.CheckStrategies()
        self.Refresh() # 重绘,执行OnPaint,强制刷新用 Update

    def CheckStrategies(self):
        # 碰撞检测等
        pass

    def OnPaint(self, e):
        # 处理重绘事件
        dc = wx.PaintDC(window=self) # device context,设备上下文,相当于画布或虚拟的屏幕,
        ctx = wx.lib.wxcairo.ContextFromDC(dc) # 获取 cairo.Context 对象,同上

        self.DrawBackground(ctx)
        self.DrawSceneObjects(ctx)

    def DrawBackground(self, ctx):
        # 绘制背景
        ctx.set_source_rgb(0, 0, 0)
        ctx.paint()
        text = "SmileBasic"
        ctx.set_font_size(40)
        _, _, w, h, _, _ = ctx.text_extents(text)
        x = (cv.BOARD_WIDTH - w) // 2
        y = (cv.BOARD_HEIGHT - h) // 2
        ctx.rectangle(x - 10, y - 10 - h, w + 20, h + 20)
        ctx.set_source_rgb(1, 0, 0)
        ctx.stroke()
        ctx.move_to(x, y)
        ctx.set_source_rgb(1, 1, 1)
        ctx.show_text(text)

    def DrawSceneObjects(self, ctx):
        # 绘制要显示的对象,如坦克、计分栏等,通过图层管理
        cv.screen.Display(ctx)

    def OnKeyDown(self, e):
        # 处理键盘按下事件
        pass

    def OnKeyUp(self, e):
        # 处理键盘弹起事件
        pass

# -*- coding: utf-8 -*-
# example.py

import random
import wx
import wx.lib.wxcairo
import cairo
from tank import Player, Enemy
from weapon import Weapon, Bullet
from obstacle import *
import board
from const import cv


class Board(board.Board):
    def InitSceneObjects(self):
        super().InitSceneObjects()

        self.player = Player(60, 50) # 实例化一个玩家坦克
        self.sceneObjects.append(self.player)

        # 初始武器系统,并装弹
        self.bullets = []
        weapon = Weapon(capacity=100, speed=2)
        for i in range(50):
            bullet = Bullet(type=3) # 装载第4行子弹
            weapon.AddBullet(bullet)
            self.bullets.append(bullet)
            self.sceneObjects.append(bullet)

            bullet = Bullet(type=6) # 装载第7行子弹
            weapon.AddBullet(bullet)
            self.bullets.append(bullet)
            self.sceneObjects.append(bullet)
        self.player.LoadWeapon(weapon) # 玩家坦克加载武器系统

        self.obstacles = []
        # 生成4个砖墙
        brick1 = Brick(10, 50)
        brick2 = Brick(58, 98)
        brick3 = Brick(106, 98)
        brick4 = Brick(230, 220)
        # 生成1个草地
        grass = Grass(300, 50)
        # 生成1个水面
        water = Water(360, 50)
        # 生成1个冰面
        ice = Ice(230, 50)
        # 生成3个小铁块
        iron1 = Iron(230, 120)
        iron2 = Iron(254, 120)
        iron3 = Iron(254, 144)
        # 生成碉堡
        blockhouse = Blockhouse(10, 220)
        # 需要把碉堡子弹也加入对象列表进行绘制和碰撞检测
        self.bullets += blockhouse.GetWeapon().GetClip()
        self.sceneObjects += blockhouse.GetWeapon().GetClip()
        # 生成两个泥坑
        mud1 = Mud(290, 220)
        mud2 = Mud(338, 220)
        # 生成两个传送阵
        transmission1 = Transmission(390, 220)
        transmission2 = Transmission(500, 120)
        transmission1.Bind(transmission2)
        self.obstacles += [brick1, brick2, brick3, brick4, grass, water,
                           ice, iron1, iron2, iron3, blockhouse, mud1, mud2, transmission1, transmission2]
        self.sceneObjects += [brick1, brick2, brick3, brick4, grass, water,
                              ice, iron1, iron2, iron3, blockhouse, mud1, mud2, transmission1, transmission2]


    def OnKeyDown(self, e):
        self.player.OnKeyDown(e)

    def OnKeyUp(self, e):
        self.player.OnKeyUp(e)


# -*- coding: utf-8 -*-
# tank.py

import random
import wx
import wx.lib.wxcairo
import cairo
from display import MovieClip
from const import cv


class Tank(MovieClip):
    def __init__(self, *args, **kwargs):
        super(Tank, self).__init__(*args, **kwargs)
        self._speed = 10 # 移动速度
        self._dx = 0 # x 轴方向
        self._dy = 0 # y 轴方向
        self._weapon = None # 武器
        self._originSpeed = 10 # 原始速度,用于恢复
        self._transTime = 0 # 等待法阵传送时间

    def Up(self):
        self._dy = -1
        self._dx = 0
        # 同一方向时需要需要排除,不然动画一直停留在第1帧
        if self._currentScene != 0:
            self.GotoAndPlay(0, 0)

    def Down(self):
        self._dy = 1
        self._dx = 0

        if self._currentScene != 1:
            self.GotoAndPlay(0, 1)

    def Left(self):
        self._dx = -1
        self._dy = 0

        if self._currentScene != 2:
            self.GotoAndPlay(0, 2)

    def Right(self):
        self._dx = 1
        self._dy = 0

        if self._currentScene != 3:
            self.GotoAndPlay(0, 3)

    def LoadWeapon(self, weapon):
        weapon.Loaded(self)
        self._weapon = weapon

    def GetWeapon(self):
        return self._weapon

    def GetSpeed(self):
        return self._speed

    def SetSpeed(self, speed):
        self._speed = speed

    def ResetSpeed(self):
        self._speed = self._originSpeed

    def GetTransTime(self):
        return self._transTime

    def SetTransTime(self, time):
        self._transTime = time


class Player(Tank):
    def __init__(self, x, y, surface=cv.surfaces["player"], rect=(0, 0, 48, 48), fps=100):
        super(Player, self).__init__(x, y, surface, rect, fps)
        self._direction = wx.WXK_UP

    def OnKeyDown(self, e):
        key = e.GetKeyCode()

        if key == wx.WXK_UP:
            self.Up()
            self._direction = wx.WXK_UP
        if key == wx.WXK_DOWN:
            self.Down()
            self._direction = wx.WXK_DOWN
        if key == wx.WXK_LEFT:
            self.Left()
            self._direction = wx.WXK_LEFT
        if key == wx.WXK_RIGHT:
            self.Right()
            self._direction = wx.WXK_RIGHT

        self._x += self._dx * self._speed
        self._y += self._dy * self._speed

        # 开火
        if key == wx.WXK_SPACE and self._weapon:
            self._weapon.Fire(True)

    def OnKeyUp(self, e):
        key = e.GetKeyCode()

        if key == wx.WXK_UP or key == wx.WXK_DOWN:
            self._dy = 0
        if key == wx.WXK_LEFT or key == wx.WXK_RIGHT:
            self._dx = 0
        if self._dx == 0 and self._dy == 0:
            self.Stop()

        # 停火
        if key == wx.WXK_SPACE and self._weapon:
            self._weapon.Fire(False)

    def GetDirection(self):
        return self._direction

class Enemy(Tank):
    def __init__(self, x, y, surface=cv.surfaces["player"], rect=(0, 0, 48, 48), fps=100, mixColor=(0, 1, 1, 0.8)):
        self._mixColor = mixColor # 自定义混合颜色
        super(Enemy, self).__init__(x, y, surface, rect, fps)
        # 初始随机方向
        self._direction = random.choice([wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT])
        self._speed = 1
        self._originSpeed = 1
        self._isPlaying = True

    def LoadFrames(self, rect):
        # 将图像载为帧前,先改变图像的色相
        surface = self._surface
        sw = surface.get_width()
        sh = surface.get_height()

        # 创建只显示坦克的 surface 作为混合层
        surface2 = cairo.ImageSurface(cairo.FORMAT_ARGB32, sw, sh)
        ctx = cairo.Context(surface2)
        ctx.set_source_rgba(*self._mixColor)
        ctx.mask_surface(surface)

        # 与目标图像混合
        ctx = cairo.Context(surface)
        ctx.set_source_surface(surface2)
        ctx.set_operator(cairo.Operator.HSL_HUE) # 将色相和目标图像色相混合
        ctx.paint()

        self._surface = surface
        super().LoadFrames(rect)

    def GetDirection(self):
        return self._direction

    def SetDirection(self, direction):
        self._direction = direction

    def ChangeDirection(self, direction):
        # 方向转换为除指定方向以外的方向,总觉得这样写不太好 加个
        # TODO
        directions = [wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT]
        directions.remove(direction)
        self._direction = random.choice(directions)

    def AutoWalk(self):
        if self._direction == wx.WXK_UP:
            self.Up()
        elif self._direction == wx.WXK_DOWN:
            self.Down()
        elif self._direction == wx.WXK_LEFT:
            self.Left()
        elif self._direction == wx.WXK_RIGHT:
            self.Right()

        # 遇边界转向,坦克宽高明明是48,可是按48处理显示不对
        # TODO
        if self._x < 0:
            self._x = 0
            self.ChangeDirection(wx.WXK_LEFT)
        elif self._x > cv.BOARD_WIDTH - 68:
            self._x = cv.BOARD_WIDTH - 68
            self.ChangeDirection(wx.WXK_RIGHT)
        else:
            self._x += self._dx * self._speed

        if self._y < 0:
            self._y = 0
            self.ChangeDirection(wx.WXK_UP)
        elif self._y > cv.BOARD_HEIGHT - 96:
            self._y = cv.BOARD_HEIGHT - 96
            self.ChangeDirection(wx.WXK_DOWN)
        else:
            self._y += self._dy * self._speed

    def Update(self, times, speed):
        # 由主程序刷新时更新
        self.AutoWalk()
        super().Update(times, speed)

# -*- coding: utf-8 -*-
# weapon.py

import wx
import wx.lib.wxcairo
import cairo
from display import MovieClip
from const import cv


class Weapon:
    def __init__(self, capacity=10, speed=20, interval=100):
        self._clip = [] # 弹夹
        self._capacity = capacity # 弹夹容量
        self._speed = speed # 发射出的子弹速度
        self._isFire = False # 开火状态
        self._tank = None # 被装载对象
        self._currentBullet = None # 当前要发射的子弹
        self._fireTime = -interval - 1 # 上次开火时间,首次开火时间必然满足
        self._fireInterval = interval # 默认开火间隔时间0.1秒

    def GetCapacity(self):
        return self._capacity

    def GetSpeed(self):
        return self._speed

    def GetBulletNum(self):
        return len(self._clip)

    def GetClip(self):
        return self._clip

    def isFire(self):
        return self._isFire

    def Loaded(self, tank):
        # 被装载
        self._tank = tank

    def AddBullet(self, bullet):
        # 装弹并返回装弹结果
        if len(self._clip) < self._capacity:
            self._clip.append(bullet)
            return True
        else:
            return False

    def Fire(self, state=True):
        # 开火或停火
        self._isFire = state
        # 判断状态及开火间隔时间是否大于武器限定间隔时间
        if state and (self._fireTime + self._fireInterval) < cv.times * cv.SPEED and len(self._clip) > 0:
            # 记录开火时间
            self._fireTime = cv.times * cv.SPEED
            self._currentBullet = self._clip.pop()
            # 根据坦克坐标和尺寸计算子弹起始位置
            direction = self._tank.GetDirection()
            x, y, w, h = self._tank.GetRect()
            _, _, bw, bh = self._currentBullet.GetRect()

            if direction == wx.WXK_UP:
                bx = x + w / 2 - bw / 2
                by = y - bh
            elif direction == wx.WXK_DOWN:
                bx = x + w / 2 - bw / 2
                by = y + h
            elif direction == wx.WXK_LEFT:
                bx = x - bw
                by = y + h / 2 - bh / 2
            elif direction == wx.WXK_RIGHT:
                bx = x + w
                by = y + h / 2 - bh / 2

            # 设置子弹方向
            self._currentBullet.SetDirection(direction)
            # 设置发射初始坐标
            self._currentBullet.SetX(bx)
            self._currentBullet.SetY(by)
            # 设置子弹速度
            self._currentBullet.SetSpeed(self._speed)
            # 发射
            self._currentBullet.Fired()


class Bullet(MovieClip):
    def __init__(self, x=-100, y=0, surface=cv.surfaces["bullets"], rect=(0, 0, 16, 16), fps=100, type=0):
        super(Bullet, self).__init__(x, y, surface, rect, fps)
        self._speed = 0 # 子弹初始速度
        self._type = type
        self._direction = None

    def SetDirection(self, direction):
        self._direction = direction

    def SetSpeed(self, speed):
        self._speed = speed

    def Fired(self):
        # 尽量只用中心对称的子弹,避免换方向发射还要做旋转处理
        self.GotoAndPlay(0, self._type)

    def Fly(self):
        if not self._destroyed and self._speed > 0:
            if self._direction == wx.WXK_UP:
                self._y -= self._speed
            elif self._direction == wx.WXK_DOWN:
                self._y += self._speed
            elif self._direction == wx.WXK_LEFT:
                self._x -= self._speed
            elif self._direction == wx.WXK_RIGHT:
                self._x += self._speed

    def Update(self, times, speed):
        self.Fly()
        super().Update(times, speed)

# -*- coding: utf-8 -*-
# obstacle.py

import math
import wx
import cairo
from display import Sprite
from weapon import Weapon, Bullet
from const import cv


class Obstacle(Sprite):
    def __init__(self, x, y, surface, parentLayer=cv.layer):
        super(Obstacle, self).__init__(x, y, surface, parentLayer=parentLayer)
        self._canCross = False # 是否可穿行
        self._canDestroy = False # 是否可打破
        self._canBulletCross = False # 是否子弹可穿行
        self._buff = False # 是否有为有特殊效果的
        self._isRemote = False # 可远程作用

    def CanCross(self):
        return self._canCross

    def CanDestroy(self):
        return self._canDestroy

    def GetLayer(self):
        return self._layer

    def CanBulletCross(self):
        return self._canBulletCross

    def HasBuff(self):
        return self._buff

    def IsRemote(self):
        return self._isRemote

    def Act(self, tank=None):
        # 与坦克的交互行为,比如加滑行 _buff,比如碉堡直接开火
        pass

    def Update(self, times, speed):
        # 用于被定时器处理函数调用刷新,静态障碍不需要重写
        pass


class Brick(Obstacle):
    # 砖墙
    def __init__(self, x, y, parentLayer=cv.layer):
        surface = self.Draw() # 直接绘制砖墙
        super(Brick, self).__init__(x, y, surface, parentLayer)
        self._canCross = False # 不可穿行
        self._canDestroy = True # 可打破
        self._canBulletCross = False # 子弹不可穿行
        self._buff = False # 没有特殊效果

    def Draw(self):
        # 绘制砖墙
        # 按坦克尺寸48创建图像,颜色不需要透明
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 48, 48)

        # 砖块是错落砌在一起的,先建一个2倍障碍宽度的矩形,作为一行砖块
        rect1 = cairo.ImageSurface(cairo.FORMAT_RGB24, 96, 12)

        # 画一个小24*12的矩形砖块,然后重复平铺
        rect2 = cairo.ImageSurface(cairo.FORMAT_RGB24, 24, 12)
        ctx = cairo.Context(rect2)
        # 填充灰色作底,水泥
        ctx.set_source_rgb(0.4, 0.4, 0.4)
        ctx.paint()
        # 画矩形,填充砖红色,砖
        ctx.set_source_rgb(0.6, 0.3, 0)
        ctx.rectangle(2, 2, 20, 8)
        ctx.fill()
        # 画线,更深的砖色,阴影
        ctx.set_source_rgb(0.42, 0.02, 0)
        ctx.set_line_width(4)
        ctx.move_to(3, 10)
        ctx.line_to(3, 3)
        ctx.line_to(22, 3)
        ctx.stroke()

        # 以 rect2 重复填充 rect1
        ctx = cairo.Context(rect1)
        pattern = cairo.SurfacePattern(rect2)
        pattern.set_extend(cairo.Extend.REPEAT)
        ctx.set_source(pattern)
        ctx.paint()

        # 把 rect1 错位绘制到 surface
        ctx = cairo.Context(surface)
        ctx.set_source_surface(rect1, -12, 0)
        ctx.paint()
        ctx.set_source_surface(rect1, 0, 12)
        ctx.paint()
        ctx.set_source_surface(rect1, -12, 24)
        ctx.paint()
        ctx.set_source_surface(rect1, 0, 36)
        ctx.paint()

        return surface


class Grass(Obstacle):
    # 草地
    def __init__(self, x, y, surface=cv.surfaces["wall_grass"], parentLayer=cv.layer3):
        super(Grass, self).__init__(x, y, surface, parentLayer)
        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果


class Water(Obstacle):
    # 水面
    def __init__(self, x, y, surface=cv.surfaces["wall_water"], parentLayer=cv.layer1):
        super(Water, self).__init__(x, y, surface, parentLayer)

        self._canCross = False # 不可穿行
        self._canDestroy = False # 不可打破
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果


class Ice(Obstacle):
    # 冰面
    def __init__(self, x, y, parentLayer=cv.layer1):
        surface = self.Draw()
        super(Ice, self).__init__(x, y, surface, parentLayer)
        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._canBulletCross = True # 子弹可穿行
        self._buff = True # 有特殊效果
        self._distance = 20 # 滑行距离

    def Draw(self):
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 48, 48)
        ctx = cairo.Context(surface)
        # 填充底色
        ctx.set_source_rgb(0.1, 0.85, 0.95)
        ctx.paint()

        # 以中心逆向旋转45度
        ctx.translate(24, 24)
        ctx.rotate(math.pi * 3 / 4)
        ctx.translate(-24, -24)

        # 画几条表示冰面纹理的线
        ctx.set_source_rgb(0, 0.64, 0.8)
        for i in range(10):
            if i % 2 == 0:
                ctx.set_dash([5, 1])
            else:
                ctx.set_dash([10, 15])

            ctx.move_to(-10, 6 * i)
            ctx.line_to(70, 6 * i)
            ctx.stroke()

        return surface

    def Act(self, tank=None):
        # 与坦克的交互行为,比如加滑行 _buff,比如碉堡直接开火
        direction = tank.GetDirection()
        x = tank.GetX()
        y = tank.GetY()
        if direction == wx.WXK_UP:
            tank.SetY(y - self._distance)
        elif direction == wx.WXK_DOWN:
            tank.SetY(y + self._distance)
        elif direction == wx.WXK_LEFT:
            tank.SetX(x - self._distance)
        elif direction == wx.WXK_RIGHT:
            tank.SetX(x + self._distance)


class Iron(Obstacle):
    # 铁块
    def __init__(self, x, y, parentLayer=cv.layer):
        surface = self.Draw()
        super(Iron, self).__init__(x, y, surface, parentLayer)
        self._canCross = False # 不可穿行
        self._canDestroy = False # 不可打破
        self._layer = 0 # 层级,0 和坦克同一层
        self._canBulletCross = False # 子弹不可穿行
        self._buff = False # 没有特殊效果

    def Draw(self):
        # 尺寸24*24
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 24, 24)
        ctx = cairo.Context(surface)
        # 底色
        ctx.set_source_rgb(0.7, 0.7, 0.7)
        ctx.paint()
        # 阴影
        ctx.move_to(0, 24)
        ctx.line_to(24, 24)
        ctx.line_to(24, 0)
        ctx.close_path()
        ctx.set_source_rgb(0.5, 0.5, 0.5)
        ctx.fill()
        # 高光
        ctx.rectangle(6, 6, 12, 12)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()
        return surface


class Blockhouse(Obstacle):
    # 碉堡
    def __init__(self, x, y, surface=cv.surfaces["wall_blockhouse"], parentLayer=cv.layer, radius=150):
        super(Blockhouse, self).__init__(x, y, surface, parentLayer)

        self._canCross = False # 不可穿行
        self._canDestroy = True # 可打破
        self._canBulletCross = False # 子弹不可穿行
        self._buff = True # 有特殊效果
        self._isRemote = True # 有远程作用
        self._radius = radius # 火力覆盖半径
        self._direction = wx.WXK_UP # 射击方向

        # 加载武器和子弹
        self._weapon = Weapon(capacity=50, speed=1, interval=500)
        self._weapon.Loaded(self)
        for i in range(50):
            self._weapon.AddBullet(Bullet(type=3))

    def LoadWeapon(self, weapon):
        weapon.Loaded(self)
        self._weapon = weapon

    def GetWeapon(self):
        return self._weapon

    def GetRadius(self):
        return self._radius

    def GetDirection(self):
        return self._direction

    def Act(self, tank=None):
        # 向坦克调整射击方向并射击
        direction = tank.GetDirection()
        tx, ty, tw, th = tank.GetRect()
        x, y, w, h = self.GetRect()

        if tx > x - tw and tx < x + w:
            if ty < y:
                # print("up")
                self._direction = wx.WXK_UP
            else:
                # print("down")
                self._direction = wx.WXK_DOWN
            self._weapon.Fire()

        if ty > y - th and ty < y + h:
            if tx < x:
                # print("left")
                self._direction = wx.WXK_LEFT
            else:
                # print("right")
                self._direction = wx.WXK_RIGHT
            self._weapon.Fire()


class Mud(Obstacle):
    # 泥坑
    def __init__(self, x, y, surface=cv.surfaces["wall_mud"], parentLayer=cv.layer1):
        super(Mud, self).__init__(x, y, surface, parentLayer)

        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._canBulletCross = True # 子弹可穿行
        self._buff = True # 有特殊效果
        self._isRemote = True # 有远程作用
        self._radius = self._width * 1.5 # 作用半径

    def GetRadius(self):
        return self._radius

    def Act(self, tank=None):
        # 离开范围
        tx, ty, tw, th = tank.GetRect()
        x, y, w, h = self.GetRect()
        if tank.GetSpeed() == 1 and (tx < x - tw or tx > x + w or ty < y - th or ty > y + h):
            tank.ResetSpeed()

    def SecondAct(self, tank=None):
        # 进入范围
        if tank.GetSpeed() > 1:
            tank.SetSpeed(1)


class Transmission(Obstacle):
    # 传送阵
    def __init__(self, x, y, parentLayer=cv.layer1):
        surface = self.Draw()
        super(Transmission, self).__init__(x, y, surface, parentLayer)
        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._layer = -1 # 层级,0 和坦克同一层
        self._canBulletCross = True # 子弹可穿行
        self._buff = True # 没有特殊效果
        self._isRemote = True # 有远程作用
        self._radius = self._width * 1.5  # 作用半径
        self._waitTime = 3000 # 等待时间3秒
        self._dest = None # 传送目的法阵

    def GetRadius(self):
        return self._radius

    def Draw(self):
        # 画个六芒星
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 48, 48)
        ctx = cairo.Context(surface)
        # 填充紫色底色
        ctx.set_source_rgb(0.27, 0.1, 0.47)
        ctx.paint()

        ctx.save()
        ctx.set_source_rgb(1, 1, 1)
        for i in range(2):
            if i == 1:
                # 对称翻转
                ctx.scale(1, -1)  # (x * dx, y * dy)
                ctx.translate(0, -48)  # 设置坐标轴原点
            # 画三角形
            ctx.move_to(0, 12)
            ctx.line_to(48, 12)
            ctx.line_to(24, 48)
            ctx.close_path()
            ctx.stroke()
        ctx.restore()

        # 紫色底不太明显,画四个角,标识下边缘
        ctx.set_source_rgb(0.6, 0.6, 0.6)
        for i in range(2):
            if i == 1:
                # 关于 x 轴对称
                ctx.scale(1, -1)
                ctx.translate(0, -48)
            for j in range(2):
                if j == 1:
                    # 关于 y 轴对称
                    ctx.scale(-1, 1)
                    ctx.translate(-48, 0)
                ctx.move_to(0, 6)
                ctx.line_to(0, 0)
                ctx.line_to(6, 0)
                ctx.close_path()
                ctx.fill()
        return surface

    def Act(self, tank=None):
        # 离开范围,等待时间归零
        tx, ty, tw, th = tank.GetRect()
        x, y, w, h = self.GetRect()
        if tx < x - tw or tx > x + w or ty < y - th or ty > y + h:
            tank.SetTransTime(0)

    def SecondAct(self, tank=None):
        # 进入法阵范围
        # 新进入,设置进入时间
        if tank.GetTransTime() == 0:
            tank.SetTransTime(cv.times * cv.SPEED)
        # 符合时间,开始传送
        if tank.GetTransTime() + self._waitTime <= cv.times * cv.SPEED:
            tank.SetX(self._dest.GetX())
            tank.SetY(self._dest.GetY())
            tank.SetTransTime(0)

    def Bind(self, transmission):
        # 法阵双向绑定
        self._dest = transmission
        transmission.SetDest(self)

    def GetDest(self):
        return self._dest

    def SetDest(self, dest):
        self._dest = dest

在 SAP BTP ABAP Environment 里把 ATC 跑成流水线:定时检查 + 告警邮件,一次打通 摘要 本文介绍了如何在SAP BTP ABAP环境中实现ATC检查的自动化流程。通过将ATC与质量体系结合,构建从检查到通知的完整闭环:使用Custom Code Analysis Project定义检查范围,通过Application Jobs实现定时调度,利用标准CDS View读取结果,最后开发通知作业发送告警邮件。文章重点阐述了云环境下不同于On-Premise的实现思路,包括Test Variant设计、作业编排模板应用等关键环节,并提供了可落地的代码示例。这种自动化方案能有效解决人工检查的滞后性 阅读详情

相关推荐

CCS20实战入门:第一个工程搭建示例

通过实际操作演示如何在ccs20中创建并配置首个开发项目,涵盖环境设置与基础调试步骤,帮助新手快速上手ccs20集成开发环境,提升嵌入式开发效率。

weixin_31185473的博客 888

python能做什么项目-这十个Python实战项目,让你瞬间读懂Python!

前言Python 是一种极具可读性和通用性的编程语言。Python 这个名字的灵感来自于英国喜剧团体 Monty Python,它的开发团队有一个重要的基础目标,就是使语言使用起来很有趣。Python 易于设置,并且是用相对直接的风格来编写,对错误会提供即时反馈,对初学者而言是个很好的选择。Python 是一种多范式语言,也就是说,它支持多种编程风格,包括脚本和面向对象,这使得它适用于通用目的。随...

q6q6q的专栏 3721

SceneSplat

目标:训练一个网络,输入高斯参数 → 输出与自然语言空间对齐的特征。考虑到标注语义的 3DGS 数据有限,作者设计了。策略,让模型在无标签场景上学到有用表征。都有一个与自然语言对齐的语义特征。这是整个方法的第一步,目标是让。

weixin_61091146的博客 1285

python 有趣的库练习

这里会将看到别人玩过有趣的实践并记录下来,会是一个长期更新的过程... 以下大部分均非原创变化自网络,只是觉得有趣便记录下来了,如有侵权,请告知删除... 自动化脚本。。。 20个必不可少的Python库 转载:http://www.python123.org/tutorials/58b41f2a28c8f30100bd41dc 读者们好。今天我将介绍20个属于我常用工具的Python...

dianxunma2886的博客 1548

PVE 9.0 保姆级安装及优化教程(换源、网络配置、远程唤醒等)【基础篇】

最近,Proxmox VE 推出了基于 Debian 13 的 9.0 版本。正巧我手头有一台闲置的老电脑,与其闲置,不如装上 PVE,让它化身 All in One,继续发挥它的余热。G4560 算是一颗 NAS 神 U,十几元的售价就能有双核四线程,比起什么 J1900 不知道强到哪里去了,尽管定位上是奔腾处理器,但性能甚至能和同代的酷睿 i3-7100 掰掰手腕,并且它还自带 HD 610 的核显,对于解码的支持也还不错。

zc_mk的博客 7512

Python爬虫实战:抓取天气数据并生成可视化图表(完整可复制代码)

工具/库核心作用选型理由requests发送HTTP请求,获取天气网页内容语法简单,上手快,支持Headers配置(反爬)解析HTML页面,提取目标天气数据无需懂复杂XPath,API直观,快速定位标签pandas数据清洗、整理(如日期/温度格式化)一键处理数据格式,与matplotlib无缝适配matplotlib生成可视化图表(折线图/饼图/柱状图)Python主流可视化库,定制性强,支持保存高清图生成随机User-Agent,应对反爬自动更新UA池,避免手动维护。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 910

python wxpython 学习示例 (1) 参考收藏

提示:python wxpython 学习示例 (1) 参考收藏 文章目录前言一、python wxpython 学习示例 (1) 参考收藏1.python wxpython hello word2.python wxpython Small editor3.python wxpython Small editor + Menu+ about +exit4.python wxpython Small editor + Menu+ about +exit + output5.python wxpython.

weixin_43709139的博客 3514

LangChain .NET:C#开发者构建AI应用的RAG框架与实战指南

大语言模型(LLM)作为当前人工智能的核心技术,通过深度学习海量文本数据,掌握了强大的语言理解和生成能力。其原理基于Transformer架构,通过自注意力机制捕捉长距离依赖关系,从而实现对复杂语义的建模。这一技术价值在于能够将非结构化的自然语言转化为可计算、可推理的表示,为各类智能应用提供认知基础。在实际工程实践中,LLM常被应用于构建检索增强生成(RAG)系统和AI智能体(Agent)。RAG通过结合外部知识库检索与LLM生成,有效提升了问答的准确性和事实性,是构建企业知识库、智能客服等场景的关键技术。

weixin_42538175的博客 705

wxPython和pycairo练习记录12

过年在家刷视频号直播时发现弹幕互动游戏,挺有意思的,刚好诠释了反射的用法。想要自己也做一个弹幕游戏,于是就有了这个基本的需求,先让弹幕滚动显示出来,直播时可以当作小挂件。

SmileBasic的博客 434

Ubuntu\linux,加载mcp2515的驱动,链接mcp2515,mcp2515的这个驱动默认在linux内核中。jetson nao、香橙派、树莓派

3. 其他文章,还要进行驱动编译就比较复杂了(不推荐)1.树莓派+MCP2515+

pvmsmfchcs的博客 1568

深度学习论文: AnomalyDINO: Boosting Patch-based Few-shot Anomaly Detection with DINOv2

通过将DINOv2模型适应到一次性和少量异常检测中,并专注于工业应用少量异常检测。实验表明,这种方法不仅与现有技术相匹敌,甚至可以在许多设置中超越它们。我们提出的仅视觉方法AnomalyDINO基于补丁相似性,能够实现图像级别的异常预测和像素级别的异常分割。该方法在方法论上很简单,无需训练,因此不需要任何额外的数据进行微调或元学习。

mingo_敏 1905

轻松一把,写个《扫雷》来玩玩(以wxPython实现)

1. 概述 相信大家对《扫雷》游戏都不陌生,它规则简单,且颇具可玩性。从技术的角度来说,这个小游戏实现起来并不太难,所以是个很好的练手题目。今天我们就尝试用wxPython来实现一个简单的《扫雷》游戏。(附件里有全部资源和源码,可供大家参考) 下图是我截取的一张游戏效果图,虽然简陋,但已能正常运行。 接下来,我们开始详细讲解。 2. 《扫雷》规则 《扫雷》的游戏规则和操作说明: 《扫雷》的基本操作区是个简单的二维地图,长宽随用户选择的游戏难度不同而不同。 地图里可操作的基本单元是小格。

夜静水寒鱼不食 满船空载月明归 877

路径规划 | 图解Informed RRT*算法(附ROS C++/Python/Matlab仿真)

Informed RRT*算法针对RRT*算法进行了采样优化,用椭圆采样代替全局均匀采样,避免了RRT*算法搜索树上产生过多冗余分支的缺陷。本文图解Informed RRT*原理,并提供ROS C++/Python/Matlab三种实现加深理解

FRIGIDWINTER的博客 7488

wxPython和pycairo练习记录10

一开始考虑不全面,后面写的时候就只有一个心思,烂代码,完成就好。代码创建控件实例是个痛苦的过程,因为做的基本都是重复的事,能用循环的最好写进循环,熟悉控件后推荐用 wxFromBuilder。

SmileBasic的博客 568

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

wxPython和pycairo练习记录2

因为要实现图像的显示和交互,今天添加两个基础的图像容器类。

SmileBasic的博客 971

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

wxPython和pycairo练习记录1

学习,分为学和习,学完了不练习,让大脑觉得不重要,会忘记。所以,我决定练习并记录 wxPython 和 pycairo 的代码编写,如果你也想试试请自行配置环境。wxPython官网:https://www.wxpython.org/pycairo官网:https://cairographics.org/pycairo/本机环境:WIN10因为没有相关完整项目经验,所以不能把全局都设计好,先从基本的迭代。...

SmileBasic的博客 283

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

wxPython和pycairo练习记录3

现在的感觉像是学武功的人,对着一本秘籍瞎练,没有师父,也没有参照,不知对错。之前写的有很多混乱的地方,比如主 fps 对对象 fps 的作用,比如 MovieClip 对象 surface 的宽高及对继承方法 GetWidth 的影响,等等没有影响现有结果的地方。唯一检验的标准是,实现出想要的效果。这次想实现的是,在四个角生成敌方坦克,自动行驶,相互碰撞转向。...

SmileBasic的博客 354

wxPython和pycairo练习记录6

这次想要实现障碍物。

SmileBasic的博客 330

wxPython和pycairo练习记录17

按理说素材图片虽然是位图但因为是纯色,手动缩放显示都没问题,不知道为什么会这样,网上解释说是"Cairo 默认使用线性插值算法进行缩放操作,而线性插值会导致图像像素的颜色被平滑地混合,从而造成图像的模糊效果。不要当过程完美主义者,很容易自我攻击。素材中的 characters.gif,用 wx.Image 加载后,调用 GetAlpha 方法发现得到不正确的透明通道值,而且 HasAlpha 得到的结果是 False,因为没有正确解释透明通道数据,RGB 数据也错了,加载后整个图片都变成了粉色。

SmileBasic的博客 897

wxPython和pycairo练习记录9

想要实现的效果是,同时按下多个方向,无论松开或按下其他键,总是响应剩余最近按下的键,这里直接用列表存储处在按下状态的按键代码。测试 wxPython 按下多个方向后松开最近按下的,之前保持按住的会失效。那就不能靠按键直接改变坐标,只改变速度方向,然后坐标的计算移动交给定时器刷新。射击按键行为也交给定时器,只改变状态,不执行操作。效果看起来还不错。之后可以看到,坦克和砖墙碰撞时的回弹问题也没有了。...

SmileBasic的博客 449

wxPython和pycairo练习记录13

之前虽然通过设置窗口样式去掉了标题栏,但是背景看起来还是有些碍眼。通过 SetTransparent 只能调整整体透明度,文字和背景都透明了。希望实现的效果是背景透明,而文字不透明。

SmileBasic的博客 560

wxPython和pycairo练习记录19

pygame.mixer.Channel 这里的声道和音频文件的声道含义不一样,这里的声道是指播放通道,0 为第一个声道。在 wxPython 中 wx.adv.Sound 只能播放 WAV 格式的文件,或者在 UI 中调用 wx.media.MediaCtrl 控件,但素材里的音频还有 OGG 格式的,没法用 wxPython 在后台播放。测试的时候,只有设置同步播放 wx.adv.SOUND_SYNC 才有声音,后面再修改,所有代码都完成了如果还是这样就再创建一个线程来播放声音。

SmileBasic的博客 1044

wxPython和pycairo练习记录4

继续写碰撞检测。

SmileBasic的博客 306

wxPython和pycairo练习记录11

了解事件机制是学习 wxPython 绕不过去的坎,结合各种资料和个人理解整理了一下,如果有问题请留言指出。

SmileBasic的博客 587

wxPython和pycairo练习记录14

继续之前的坦克游戏。碰撞检测技术像什么四叉树、凸包,看不懂啊,所以先从成熟的 pygame 框架里抠出几个能用到的。之前写坦克游戏并没有明确组合多个 Sprite 对象,还是先只考虑独立的 Sprite 吧。这样,需要用到和改写的方法只有 `collide_rect、collide_rect_ratio、collide_circle、collide_circle_ratio、collide_mask`。

SmileBasic的博客 263

wxPython和pycairo练习记录15

像素:是指在由一个数字序列表示的图像中的一个最小单位,称为像素。这里的像素并非我们平时所说的眼睛看到的点,它更像是屏幕上的一个网格,不同的显示设备可能有不同大小的格子。在 Python 中,我们可以方便地通过一些库读取图像的某个像素值。这个例子中,返回的像素值是 RGBA 数值元组。

SmileBasic的博客 416
上一篇: wxPython和pycairo练习记录7
下一篇: wxPython和pycairo练习记录9
SmileBasic
博客等级 码龄5年 347粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值