wxPython和pycairo练习记录6

实战指南:基于ELK与Grafana构建天融信防火墙日志可视化看板 本文详细介绍了如何基于ELK与Grafana构建天融信防火墙日志可视化看板,包括环境准备、日志收集与解析配置、Elasticsearch存储优化、Grafana看板设计等关键步骤。通过实战案例和优化技巧,帮助用户高效处理天融信防火墙日志,实现安全态势的可视化监控与分析。 阅读详情

wxPython和pycairo练习记录6

这次想要实现障碍物。

6.1 障碍类

90坦克游戏里障碍有砖墙、草地、铁块、冰面、水面。

砖墙:不能穿行,能打破
草地:能穿行,不能打破,坦克能躲在草里,视觉上坦克在草地下面一层
铁块:不能穿行,不能打破,特殊子弹能打破
冰面:能穿行,不能打破,视觉上坦克在冰面上一层,坦克通过时会滑行
水面:不能穿行,不能打破,子弹能穿过

再编几个:
反弹墙:坦克碰撞就被反弹一段距离
碉堡:给障碍加载之前写的武器系统
动感超人:坦克靠近一定距离就发射动感光波

障碍本身是单帧的,选择继承 Sprite ,按上面的分析,抽象出障碍类。

class Obstacle(Sprite):
    def __init__(self, x, y, surface):
        # 重写 Sprite 的构造函数, 直接传入 surface
        self._x = x
        self._y = y
        self._destroyed = False # 销毁状态
        self._surface = surface
        # 获取图像尺寸
        self._width = self._surface.get_width()
        self._height = self._surface.get_height()

        self._canCross = False # 是否可穿行
        self._canDestroy = False # 是否可打破
        self._layer = 0 # 层级,0 和坦克同一层,-1 下层, 1 上层
        self._canBulletCross = False # 是否子弹可穿行
        self._buff = 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 Act(self, tank=None):
        # 与坦克的交互行为,比如加滑行 _buff,比如碉堡直接开火
        pass

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

6.2 砖墙

砖墙如果使用图片素材比较简单,只需要设置几项属性。砖墙应该不算复杂,这里使用 pycairo 绘制试试。

class Brick(Obstacle):
    # 砖墙
    def __init__(self, x, y):
        surface = self.Draw() # 直接绘制砖墙
        super(Brick, self).__init__(x, y, surface)
        self._canCross = False # 不可穿行
        self._canDestroy = True # 可打破
        self._layer = 0 # 层级,0 和坦克同一层
        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

不考虑属性做下测试,碰撞检测,需要检测子弹和砖墙碰撞,坦克和砖墙碰撞。砖墙是静止的,所以坦克和它碰撞只能是在前进方向。换个简单环境,只保留玩家坦克。
在这里插入图片描述

玩家坦克和砖墙碰撞,期望的效果是直接停止,这里有像塑料一样弹回来的感觉。在理想情况下,先碰撞检测更新数据,然后绘制,这样是不会出现问题的。

这里用 print 分别在绘制前、碰撞检测前后输出玩家坦克矩形 wx.Rect(x, y, width, height)。

碰撞检测 (60, 60, 48, 48)
检测到碰撞 (60, 60, 48, 48)
碰撞重置为 (60, 50, 48, 48)
绘制 (60, 50, 48, 48)
碰撞检测 (60, 50, 48, 48)
绘制 (60, 60, 48, 48)

已经知道玩家坦克的移动速度是10像素。理想流程:坦克移动,碰撞检测,绘制。从上面的最后两条输出可以发现,实际发生的流程:坦克移动,碰撞检测,坦克移动,绘制。

网上查询是说 wxPython 控件的 Refresh 方法,不会立即执行重绘,强制立即重绘可以使用 Update 方法。改为 Update 倒是没有出现Q弹的效果,但是坐标又会出现问题。这里暂时就保留这种弹的效果吧。

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

import random
import math
import wx
import wx.lib.wxcairo
import cairo
import board
from display import MovieClip
from tank import Player, Enemy
from weapon import Weapon, Bullet
from obstacle import Brick

class cv(board.cv):
    """
    常量,用类属性避免使用全局变量
    """
    # 面板尺寸
    BOARD_WIDTH = 600
    BOARD_HEIGHT = 400


class Board(board.Board):
    def DrawBackground(self, ctx):
        super().DrawBackground(ctx)

        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

        # 文字是以首个字左下角坐标定位,而矩形是以左上角定位,y轴相差文字的高度,不需要考虑线条宽度。
        # 另外PaintDC是不含标题栏的。
        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 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) # 玩家坦克加载武器系统

        # 生成4个砖墙
        self.bricks = []
        brick1 = Brick(10, 50)
        brick2 = Brick(58, 98)
        brick3 = Brick(106, 98)
        brick4 = Brick(230, 220)
        self.bricks += [brick1, brick2, brick3, brick4]
        self.sceneObjects += [brick1, brick2, brick3, brick4]

    def DrawSceneObjects(self, ctx):
        print("绘制", self.player.GetRect())
        for so in self.sceneObjects:
            if not so.IsDestroyed():
                ctx.set_source_surface(so.GetSurface(), so.GetX(), so.GetY())
                ctx.paint()

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

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

    def CheckStrategies(self):
        self.CheckCollisions()

    def CheckCollisions(self):
        # 检测子弹砖墙碰撞,碰撞后都销毁
        print("碰撞检测", self.player.GetRect())
        for brick in self.bricks[:]:
            rect = brick.GetRect()
            for bullet in self.bullets[:]:
                if rect.Intersects(bullet.GetRect()):
                    brick.Destroy()
                    self.bricks.remove(brick)
                    self.sceneObjects.remove(brick)

                    bullet.Destroy()
                    self.bullets.remove(bullet)
                    self.sceneObjects.remove(bullet)
                    break

            # 检测坦克和砖墙碰撞,碰撞后坦克位置重置
            if rect.Intersects(self.player.GetRect()):
                print("检测到碰撞", self.player.GetRect())
                direction = self.player.GetDirection()
                x, y, w, h = rect
                _, _, pw, ph = self.player.GetRect()
                if direction == wx.WXK_UP:
                    self.player.SetY(y + h)
                elif direction == wx.WXK_DOWN:
                    self.player.SetY(y - ph)
                elif direction == wx.WXK_LEFT:
                    self.player.SetX(x + w)
                elif direction == wx.WXK_RIGHT:
                    self.player.SetX(x - pw)
                print("碰撞重置为", self.player.GetRect())


6.3 草地和水面

草地在坦克和子弹上层,水面在子弹下层,用来测试层级的问题。用 pycairo 绘制的时候,后绘制的图像在上层,用 set_operator(cairo.Operator.DEST_OVER) 没有达到想要的调整图层效果。这样只能用简单粗暴的办法,在坦克绘制前后各加一个绘制障碍的操作,用障碍的属性 layer 来判断是否绘制。
在这里插入图片描述

class Grass(Obstacle):
    # 草地
    def __init__(self, x, y, path="wall_grass.png"):
        # 从文件加载 surface
        try:
            surface = cairo.ImageSurface.create_from_png(path)
        except IOError as e:
            print(e.message)

        super(Grass, self).__init__(x, y, surface)

        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._layer = 1 # 层级,0 和坦克同一层
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果


class Water(Obstacle):
    # 水面
    def __init__(self, x, y, path="wall_water.png"):
        # 从文件加载 surface
        try:
            surface = cairo.ImageSurface.create_from_png(path)
        except IOError as e:
            print(e.message)

        super(Water, self).__init__(x, y, surface)

        self._canCross = False # 不可穿行
        self._canDestroy = False # 不可打破
        self._layer = -1 # 子弹可以在水面上层飞过
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果
        
    def DrawSceneObjects(self, ctx):
        # print("绘制", self.player.GetRect())
        # 后绘制的对象在上层显示
        for obstacle in self.obstacles:
            if obstacle.GetLayer() == -1:
                self.DrawSprite(ctx, obstacle)

        self.DrawSprite(ctx, self.player)

        for bullet in self.bullets:
            self.DrawSprite(ctx, bullet)

        for obstacle in self.obstacles:
            if obstacle.GetLayer() >= 0:
                self.DrawSprite(ctx, obstacle)

6.4 冰面

冰面能带给坦克滑行 buff,而坦克可穿行,之前写的是可穿行就不做坦克和障碍的碰撞检测。需要搞清楚的逻辑是,障碍 buff 是对坦克作用的,所以必须检测和坦克的碰撞,不管是直接碰撞还是接近障碍范围碰撞。
在这里插入图片描述

class Ice(Obstacle):
    # 冰面
    def __init__(self, x, y):
        surface = self.Draw()
        super(Ice, self).__init__(x, y, surface)
        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._layer = -1 # 层级,0 和坦克同一层
        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)

6.5 代码及效果

在这里插入图片描述

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

import math
import wx
import cairo
from display import Sprite


class Obstacle(Sprite):
    def __init__(self, x, y, surface):
        # 重写 Sprite 的构造函数, 直接传入 surface
        self._x = x
        self._y = y
        self._destroyed = False # 销毁状态
        self._surface = surface
        # 获取图像尺寸
        self._width = self._surface.get_width()
        self._height = self._surface.get_height()

        self._canCross = False # 是否可穿行
        self._canDestroy = False # 是否可打破
        self._layer = 0 # 层级,0 和坦克同一层,-1 下层, 1 上层
        self._canBulletCross = False # 是否子弹可穿行
        self._buff = 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 Act(self, tank=None):
        # 与坦克的交互行为,比如加滑行 _buff,比如碉堡直接开火
        pass

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


class Brick(Obstacle):
    # 砖墙
    def __init__(self, x, y):
        surface = self.Draw() # 直接绘制砖墙
        super(Brick, self).__init__(x, y, surface)
        self._canCross = False # 不可穿行
        self._canDestroy = True # 可打破
        self._layer = 0 # 层级,0 和坦克同一层
        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, path="wall_grass.png"):
        # 从文件加载 surface
        try:
            surface = cairo.ImageSurface.create_from_png(path)
        except IOError as e:
            print(e.message)

        super(Grass, self).__init__(x, y, surface)

        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._layer = 1 # 层级,0 和坦克同一层
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果


class Water(Obstacle):
    # 水面
    def __init__(self, x, y, path="wall_water.png"):
        # 从文件加载 surface
        try:
            surface = cairo.ImageSurface.create_from_png(path)
        except IOError as e:
            print(e.message)

        super(Water, self).__init__(x, y, surface)

        self._canCross = False # 不可穿行
        self._canDestroy = False # 不可打破
        self._layer = -1 # 子弹可以在水面上层飞过
        self._canBulletCross = True # 子弹可穿行
        self._buff = False # 没有特殊效果

class Ice(Obstacle):
    # 冰面
    def __init__(self, x, y):
        surface = self.Draw()
        super(Ice, self).__init__(x, y, surface)
        self._canCross = True # 可穿行
        self._canDestroy = False # 不可打破
        self._layer = -1 # 层级,0 和坦克同一层
        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)

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

import random
import math
import wx
import wx.lib.wxcairo
import cairo
import board
from display import MovieClip
from tank import Player, Enemy
from weapon import Weapon, Bullet
from obstacle import Brick, Grass, Water, Ice

class cv(board.cv):
    """
    常量,用类属性避免使用全局变量
    """
    # 面板尺寸
    BOARD_WIDTH = 600
    BOARD_HEIGHT = 400


class Board(board.Board):
    def DrawBackground(self, ctx):
        super().DrawBackground(ctx)

        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

        # 文字是以首个字左下角坐标定位,而矩形是以左上角定位,y轴相差文字的高度,不需要考虑线条宽度。
        # 另外PaintDC是不含标题栏的。
        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 InitSceneObjects(self):
        super().InitSceneObjects()
        self.InitPlayer()
        self.InitObstacles()

    def InitPlayer(self):
        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) # 玩家坦克加载武器系统

    def InitObstacles(self):
        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)
        self.obstacles += [brick1, brick2, brick3, brick4, grass, water, ice]
        self.sceneObjects += [brick1, brick2, brick3, brick4, grass, water, ice]

    def DrawSceneObjects(self, ctx):
        # print("绘制", self.player.GetRect())
        # 后绘制的对象在上层显示
        for obstacle in self.obstacles:
            if obstacle.GetLayer() == -1:
                self.DrawSprite(ctx, obstacle)

        self.DrawSprite(ctx, self.player)

        for bullet in self.bullets:
            self.DrawSprite(ctx, bullet)

        for obstacle in self.obstacles:
            if obstacle.GetLayer() >= 0:
                self.DrawSprite(ctx, obstacle)

    def DrawSprite(self, ctx, sprite):
        ctx.set_source_surface(sprite.GetSurface(), sprite.GetX(), sprite.GetY())
        ctx.paint()

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

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

    def CheckStrategies(self):
        self.CheckCollisions()

    def CheckCollisions(self):
        # 检测子弹砖墙碰撞
        # print("碰撞检测", self.player.GetRect())
        for obstacle in self.obstacles[:]:
            # 子弹可穿行,则跳过检测
            if obstacle.CanBulletCross():
                continue

            rect = obstacle.GetRect()
            for bullet in self.bullets[:]:
                if rect.Intersects(bullet.GetRect()):
                    # 如果可摧毁,则碰撞后子弹和障碍都销毁
                    if obstacle.CanDestroy():
                        obstacle.Destroy()
                        self.obstacles.remove(obstacle)
                        self.sceneObjects.remove(obstacle)

                    bullet.Destroy()
                    self.bullets.remove(bullet)
                    self.sceneObjects.remove(bullet)
                    break

        # 检测坦克和砖墙碰撞,碰撞后坦克位置重置
        for obstacle in self.obstacles[:]:
            # 坦克可穿行并且没有特殊效果,则跳过检测
            if obstacle.CanCross() and not obstacle.HasBuff():
                continue

            rect = obstacle.GetRect()
            if rect.Intersects(self.player.GetRect()):
                # print("检测到碰撞", self.player.GetRect())

                # 如果有特殊效果则只不执行后续位置重置
                if obstacle.HasBuff():
                    obstacle.Act(self.player)
                    continue

                direction = self.player.GetDirection()
                x, y, w, h = rect
                _, _, pw, ph = self.player.GetRect()
                if direction == wx.WXK_UP:
                    self.player.SetY(y + h)
                elif direction == wx.WXK_DOWN:
                    self.player.SetY(y - ph)
                elif direction == wx.WXK_LEFT:
                    self.player.SetX(x + w)
                elif direction == wx.WXK_RIGHT:
                    self.player.SetX(x - pw)
                # print("碰撞重置为", self.player.GetRect())


手把手教你搭建三端口DC-DC变换器(含Boost+Buck电路实战) 本文详细介绍了如何从零开始搭建三端口DC-DC变换器,涵盖Boost和Buck电路的硬件设计、元器件选型、电路板布局、焊接技巧、控制系统设计与调试等关键步骤。通过实战案例和优化建议,帮助工程师避开常见陷阱,提升系统效率和稳定性,适用于光伏、储能等电力电子应用场景。 阅读详情

相关推荐

转录组PCA分析3大常见误区:数据标准化、离群样本与批次效应处理

本文深入探讨转录组PCA分析中的三大常见误区:数据标准化、离群样本识别与批次效应处理。通过实战案例和R代码演示,揭示如何优化PCA结果以提升生物学解释力,特别关注数据标准化策略的选择和批次校正方法的应用,帮助研究者避免常见陷阱并获得可靠的转录组分析结果。

weixin_28362173的博客 373

wxPython和pycairo练习记录12

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

SmileBasic的博客 434

Linux-BSP-WIFI驱动移植与调试实战——以RTL8189FS为例

本文以RTL8189FS为例,详细介绍了Linux BSP中WiFi驱动移植与调试的全流程。涵盖硬件原理图分析、内核配置、设备树适配、驱动编译加载及问题排查等关键步骤,提供实用的调试技巧和解决方案,帮助开发者高效完成嵌入式WiFi驱动开发。

moon9的博客 399

wxPython:调用OCR模块实现图片识别

(读者电脑中应安装有Canopy) 先下载tesseract-ocr模块: 将其安装在Canopy那个盘。 安装了之后,下载代码:pytesser_v0.0.1.zip 将代码解压至......\Canopy\User\Lib\site-packages 然后在Canopy内运行pytesser.py即可。 注意:要将代码中11,237,301,311行的路径修改成读者本机路径。

每天都要进步哦~ 1377

STM32H750实战:用TIM+DMA驱动ADC/DAC实现音频回环(附CubeMX配置避坑指南)

本文详细介绍了在STM32H750上构建高保真、低延迟音频链路的实战方法。通过配置TIM定时器作为硬件节拍器,精准触发ADC采集与DAC播放,并利用DMA实现数据的自动搬运,全程无需CPU干预。文章重点剖析了CubeMX配置中的关键步骤与常见“坑点”,如ADC的Conversion Data Management模式选择、DMA的FIFO与Burst模式配置,以及STM32H7系列必须开启ADC全局中断等,并提供了完整的代码实现与调试验证指南。

1057

wxPython和pycairo练习记录17

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

SmileBasic的博客 897

AU混响终极指南:从‘干声’到‘空间感’,用总音轨和发送技巧打造专业人声

本文详细介绍了如何在Adobe Audition(AU)中使用混响效果器,通过总音轨和发送技巧将‘干声’转化为具有专业‘空间感’的人声。从混响基础原理到总线架构设置,再到精细控制和自动化调整,全面解析了提升音频质量的实用技巧,适用于音乐制作、播客和内容创作。

weixin_30512089的博客 333

wxPython 示例项目指南

wxPython 示例项目指南 本指南将带您深入了解 wxPython-examples 开源项目,该项目由一系列wxPython示例构成,旨在帮助开发者学习如何利用wxPython库创建图形用户界面应用。我们将逐一解析其关键组件:目录结构、启动文件以及潜在的配置文件。 1. 项目目录结构及介绍 该开源项目遵循了清晰的目录结构来组织各个示例应用程序。下面是核心的目录结构概览: wxPython-e...

gitblog_00947的博客 380

CVE-2021-3156本地提权漏洞复现

受影响版本 sudo: 1.8.2 - 1.8.31p2 sudo: 1.9.0 - 1.9.5p1 测试 我这里是在本地centos7上进行测试。 需要目标机器上存在python编译器、目标主机出网或者能上传文件到目标主机。 以非root用户登录系统,并运行如下命令: sudoedit -s / 通常响应一个以 sudoedit: / 开头的报错,多半存在漏洞。 利用 提权脚本: https://www.o2oxy.cn/wp-content/uploa

The current road is different, love needs to distinguish between right and wrong 3782

ASPICE(Automotive SPICE)流程落地,研发管理工具自研,如何从量化数据体现研发效率提升的

摘要: ASPICE流程显著提升研发效率,主要体现在质量、成本和时间三个维度。关键量化指标包括:缺陷率降低30%-60%,维护工作量减少28%,研发周期缩短10%-20%,需求变更率降低58%,代码评审效率提升300%。初期开发成本虽增加9%,但长期回报率高。核心方法包括缺陷预防前移、需求精准管理和流程标准化与自动化。建立量化度量体系需覆盖缺陷密度、需求稳定性、进度偏差等指标,并依托工具链支持数据收集。实施ASPICE是短期投入换取长期质量提升和成本优化的高效投资。

汽车软件开发 186

wxPython和pycairo练习记录18

Font.py 中 Font 类居然继承 Spritesheet 类来加载字符对应图片,还和 Sprites 类的功能差不多,总感觉哪里不太对。基本都没法写测试啊,后面待完成的是声音、鼠标键盘输入、碰撞检测、实体和主要显示类:面板、关卡、菜单、暂停画面。Camera.py 摄像机,这里的功能是记录坐标,跟随实体移动坐标。

SmileBasic的博客 396

当C#遇见声音魔法:从麦克风到文字的0.1秒奇幻漂流

为什么选300Hz?因为人声基频通常在85-255Hz,但清脆的“啊”可能到2kHz,所以300Hz是安全区!(参考知识库[1])

墨夶的博客 365

wxPython和pycairo练习记录10

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

SmileBasic的博客 568

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

wxPython和pycairo练习记录2

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

SmileBasic的博客 971

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

wxPython和pycairo练习记录8

经过障碍物的编写,发现需要停止前进并重新梳理一下。1.新的检测项加入时,已经在向混乱发展。是时候用上封装变化的编程工具,用策略模式或状态模式重写。2.显示,在基类引入层级和优先级,不同层低层级先绘制,同层高优先级先绘制。3.范围的碰撞和本身的碰撞的作用顺序。还有像已经实现的碉堡,只有一个外层碰撞区域,如果要多加几层呢,最外层出现坦克,射速为1,再近一层射速为5,最近一层射速达到20。

SmileBasic的博客 387

visio安装包

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

wxPython和pycairo练习记录1

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

SmileBasic的博客 283

wxPython和pycairo练习记录3

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

SmileBasic的博客 354

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

继续写障碍,选几个典型的来写。有个问题,有调用图片文件的类,每次生成新实例,都重新加载一次图片。应该把素材统一加载为全局变量,具体使用只需要引用素材变量。积重难返,记住这个教训,以后写其他的再注意吧。...

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值