pygame里实现导弹追踪效果,同时对python的指针机制有一点点思考

       最近,儿子一直缠着让我把之前给他编写的游戏重做一下,要加一些功能.但是因为之前写代码的时候刚学会python,当时的想法就是能跑就行,现在回头看来,代码的可维护性几乎为零.所以没办法只能冲头再来,重构了几乎所有代码.在编写的时候遇到了一个有意思的问题,儿子让我给游戏添加一种带追踪能力的导弹.导弹不仅要能追踪目标不断修正轨迹向着目标前进,同时导弹的弹头要一直朝向目标,我一听,这不就是弹道导弹嘛,有意思,整!

        当时我想,这还不简单,在追踪的时候顺手把本体和目标之间连线的斜率计算出来不久完了吗?于是说干就干,但是当我到了真正编写的时候还是发现了一些问题.今天就来和大家分享一下.

        首先呢,路径追踪很简单,我之前写过一篇相关的文章可以参考这个http://t.csdnimg.cn/soVGy,所以路径这里咱们就跳过.

        这篇文章主要就聊聊如何实现导弹通过旋转始终指向目标.

先来看一张示意图

如图,当A点向B移动时是沿着AB移动,所以只要计算出AB的斜率时就会得到第一个旋转角度,当A移动到A`点时追踪目标变为了C,这时算出A`C的斜率就能得到第二个旋转角度,如此循环往复,无论被追踪的目标如何变化,我们只要算出当前目标和本身的斜率就能得出要旋转的角度了.明白了实现旋转的方法以后,我们就开始写代码.这里多说一句,其实一开始我在这里思考了很长时间,主要的问题是我的一个闪念,既然正切函数能够得到斜率,那么正弦余弦甚至正割余割,也应该能到倾斜角度(其实是废话,利用推导公式本来就可以推导出的,但我也不知道为什么非要跟自己较劲,花了一整天时间去复习初中数学),但是,正弦余弦有一个问题,就是要判断象限.我甚至写了一大段"if"函数去判断象限.知道我发现math模块里的atan2()这个方法,我才明白,我之前想多了.这个方法不用考虑象限,只需要输入对边临边的值就会自动判断象限,从而得出当前的弧度.

代码如下:

import math

import pygame,sys
from math import *
pygame.init()
screen=pygame.display.set_mode((800,700),0,32)
pygame.display.set_caption("弹道导弹")
clock = pygame.time.Clock()

class Sprite1(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load('2.png')
        self.rect = self.image.get_rect()

    def update(self, *args, **kwargs):
        self.rect.center = pygame.mouse.get_pos()

class Sprite2(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.mumimage = pygame.image.load('1.png')
        self.image = self.mumimage
        # print(id(self.mumimage), id(self.image))
        self.rect = self.image.get_rect()
        self.speed = 3

    def update(self, target):
        deltax = target.rect.centerx - self.rect.midtop[0]
        deltay = target.rect.centery - self.rect.midtop[1]
        distance = math.sqrt(deltax ** 2 + deltay ** 2)
        if distance != 0:
            angle = math.degrees(math.atan2(-deltay, deltax))
            self.image = pygame.transform.rotate(self.mumimage, angle - 90)
            # print(id(self.mumimage), id(self.image))
            stepx = self.speed * deltax / distance
            stepy = self.speed * deltay / distance
            self.rect.centerx += stepx
            self.rect.centery += stepy




sprite1 = Sprite1()
sprite2 = Sprite2()
sprites = pygame.sprite.Group()
sprites.add(sprite1,sprite2)
while True:
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            sys.exit()

    screen.fill((0,0,0))
    sprites.update(sprite1)
    sprites.draw(screen)
    pygame.draw.rect(screen, (0,255,0), sprite2.rect, 2)


    pygame.display.update()
    clock.tick(60)

运行效果如下

其实写到这里,我要的目的目的其实已经达到了.但是,在写代码的时候我发现一个问题.

可以看到,我在编写sprite2的时候,定义了一个self.mumimage对象,然后又创建了一个self.image对象指向self.mumimage.这样做,主要是因为python的变量赋值其实是将变量指向了对象的内存地址,也就是说python里的变量都是指针变量.

pygame.transform.rotate()这个方法会生成一个新的对象,同时在内存中开辟一个新的地址.

如果我们直接使用self.image = pygame.transform.rotate(self.mumimage, angle - 90),那么就会产生递归,self.image 会不停的迭代self.image会不停的指向新地址而pygame.transform.rotate()方法会使用self.image迭代的新对象.这就产生了无限递归,直至内存泄漏,程序崩溃.下面举个例子.当我们删掉self.mumimage这个变量,直接定义self.image = pygame.image.load('1.png'),那么运行会得到以下结果.

所以,在这里我们必须要先声明一个变量self.mumimage,然后用self.image捕获self.mumimage迭代后的对象.这样才能保证pygame.transformer.rotate()这个方法一直作用与self.mumimage而不会产生无限的递归导致程序崩溃.

最后是文章中用到的素材

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用PythonPygame可以实现各种动态效果,例如游戏中的动画、粒子效果等。下面是一些基本的步骤: 1. 安装Pygame库:首先需要安装Pygame库,可以使用pip命令进行安装。在命令行中输入以下命令: ``` pip install pygame ``` 2. 导入Pygame库:在Python代码中导入Pygame库,以便使用其中的函数和类。示例代码如下: ```python import pygame ``` 3. 初始化Pygame:在代码的开始部分,需要初始化Pygame。示例代码如下: ```python pygame.init() ``` 4. 创建窗口:使用Pygame创建一个窗口来显示动态效果。示例代码如下: ```python screen = pygame.display.set_mode((width, height)) ``` 5. 设置游戏循环:使用一个循环来更新游戏状态和绘制图像。示例代码如下: ```python running = True while running: # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 更新游戏状态 # 绘制图像 # 刷新屏幕 pygame.display.flip() ``` 6. 处理事件:在游戏循环中处理各种事件,例如键盘按键、鼠标点击等。示例代码如上述所示。 7. 更新游戏状态:根据游戏逻辑更新游戏状态,例如移动角色、检测碰撞等。 8. 绘制图像:使用Pygame提供的绘图函数来绘制图像,例如绘制角色、背景等。 9. 刷新屏幕:在每次循环结束后,使用`pygame.display.flip()`函数来刷新屏幕,使得更新后的图像显示出来。 这只是一个简单的示例,具体的实现方式和效果取决于你的需求和创意。你可以通过查阅Pygame的官方文档和示例代码来学习更多关于Pygame的用法和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值