h5或flash中,可以直接对矢量对象,比如line, rectange旋转,但是pygame中,仅支持对image旋转,本以为这个是很简单的事情,但是发现还是有很多小猫腻的,记录一下:
先看一个错误的版本:
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
angle = 1 screen = pygame.display.set_mode(SIZE)
leaf = pygame.image.load("leaf.png")
leafRect = leaf.get_rect()
# 定位到舞台中心
leafRect = leafRect.move((WIDTH - leafRect.width) / 2, (HEIGHT - leafRect.height) / 2) clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() # 旋转图片
leaf = pygame.transform.rotate(leaf, angle)
angle += 1 # 默认背景为白色,所以每渲染一帧,要对背景重新填充,否则会有上一帧的残影
screen.fill(BLACK)
# 将旋转后的图象,渲染矩形里
screen.blit(leaf, leafRect)
# 正式渲染
pygame.display.update()
# 控制帧数<=100
clock.tick(100)
leaf-01
代码中的leaf.png图例如下:
跑一把:
这明显跟我想的不一样!代码里并没有对叶子做移动操作,只是每帧旋转1度而已,为啥它要飘到舞台之外?
仔细review了下代码,25行:leaf = pygame.transform.rotate(leaf, angle) 这里有问题,pygame在这方面做得说实话不算太好,字面上的意思,这行的效果,应该是每次在原来的基础上,继续加速旋转(因为每次angle+1,相当于每帧旋转角度越来越大)。
需要用一个新变量存储旋转后的图片,参考下面的代码:
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
angle = 1 screen = pygame.display.set_mode(SIZE)
leaf = pygame.image.load("leaf.png")
leafRect = leaf.get_rect()
leafRect = leafRect.move((WIDTH - leafRect.width) / 2, (HEIGHT - leafRect.height) / 2) clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() keys = pygame.key.get_pressed() # 旋转图片(注意:这里要搞一个新变量,存储旋转后的图片)
newLeaf = pygame.transform.rotate(leaf, angle)
angle += 1 screen.fill(BLACK)
screen.blit(newLeaf, leafRect)
pygame.display.update()
clock.tick(100)
leaf-02
总算不乱跑,开始正经的旋转了:
但是仔细观察,还是有点小问题,旋转过程中,叶子的中心位置总在晃动,预期效果最好是旋转过程中,中心点不变。至于晃动的原因,叶子图片并不是一个圆形,pygame中,任何一个Surface对象,总有一个外切的矩形对象(通过get_rect()方法可以获得),图片旋转后,这个外切Rect对象的尺寸跟着变化,导致中心点也变化了。
校正方法如下(为了方便观察,我们把叶子外围正切的矩形也画出来)
做为对比,刚才的中心晃动版本,也把矩形画出来,参考以下代码:
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
angle = 1 screen = pygame.display.set_mode(SIZE)
leaf = pygame.image.load("leaf.png")
leafRect = leaf.get_rect()
leafRect = leafRect.move((WIDTH - leafRect.width) / 2, (HEIGHT - leafRect.height) / 2) clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() keys = pygame.key.get_pressed() # 旋转图片(注意:这里要搞一个新变量,存储旋转后的图片)
newLeaf = pygame.transform.rotate(leaf, angle)
newRect = newLeaf.get_rect()
angle += 1 screen.fill(BLACK)
screen.blit(newLeaf, leafRect)
pygame.draw.rect(screen, (255, 0, 0), leafRect, 1)
pygame.draw.rect(screen, (0, 255, 0), newRect, 1)
pygame.display.update()
clock.tick(100)
leaf-02-with-rect
校正中心点后的版本:
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
angle = 1 screen = pygame.display.set_mode(SIZE)
leaf = pygame.image.load("leaf.png")
leafRect = leaf.get_rect()
leafRect = leafRect.move((WIDTH - leafRect.width) / 2, (HEIGHT - leafRect.height) / 2) clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() keys = pygame.key.get_pressed() # 旋转图片(注意:这里要搞一个新变量,存储旋转后的图片)
newLeaf = pygame.transform.rotate(leaf, angle)
# 校正旋转图片的中心点
newRect = newLeaf.get_rect(center=leafRect.center)
angle += 1 screen.fill(BLACK)
# 这里要用newRect区域,绘制图象
screen.blit(newLeaf, newRect)
pygame.draw.rect(screen, (255, 0, 0), leafRect, 1)
pygame.draw.rect(screen, (0, 255, 0), newRect, 1)
pygame.display.update()
clock.tick(100)
leaf-03
运行后的对照图如下(左侧为中心晃动的版本,右侧为中心点不变的版本)
思考一下:为什么左侧的图,绿色的矩形框,一直在左上角,而右侧的绿矩形框,会在中心?
答案:Rect对象默认生成时,其left,top属性都是0, 所以旋转后的新图片,其外切矩形一直是在(0,0)位置,但是校正后的版本,get_rect(center=...)这里指定了中心点,所以newRect对象被对齐到跟红色Rect一样的中心位置。
(题外话:至于需要不需要中心点校正,完全看游戏场景,就本例而言,如果只是模拟一片树叶落下,好象中心点晃动,也并不影响视觉感受,所以下面的示例,均没有做中心点校正处理)
结合之前学到的东西,再加点趣味性,让叶子在舞台上飘动起来,同时加入边界碰撞检测,但是要注意:叶子在旋转过程中,外要的矩形尺寸,也会随之变化(这会影响边界检测),为了观察方便,在叶子外面画一个框框。
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
RED = 255, 0, 0
SPEED = [1, 1]
angle = 1 screen = pygame.display.set_mode(SIZE)
originLeaf = pygame.image.load("leaf.png")
originRect = originLeaf.get_rect() clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() # 利用矩形对象的move方法,让rect移动
originRect = originRect.move(SPEED[0], SPEED[1])
# 注意:这里一定要用一个新变量newLeaf,保存旋转后的image对象
newLeaf = pygame.transform.rotate(originLeaf, angle)
angle += 1 # 注意:这里要定义一个新rect对象,因为图象旋转后,其外切的矩形尺寸会变化
newRect = newLeaf.get_rect()
# 默认的newRect位置在(0,0),要实现矩形外框跟随,必须赋值到新位置
newRect.left, newRect.top = originRect.left, originRect.top # 左右边界反弹的处理
if newRect.left <= 0 or newRect.right >= WIDTH:
SPEED[0] = -SPEED[0] # 上下边界反弹的处理
if newRect.top <= 0 or newRect.bottom >= HEIGHT:
SPEED[1] = -SPEED[1] # 默认背景为白色,所以每渲染一帧,要对背景重新填充,否则会有上一帧的残影
screen.fill(BLACK)
# 画新矩形
pygame.draw.rect(screen, RED, newRect, 1)
# 将旋转后的图象,渲染到新矩形里
screen.blit(newLeaf, originRect)
# 正式渲染
pygame.display.update()
# 控制帧数<=100
clock.tick(100)
基本达到效果了,但是细心观察的话,发现右边界和下边界,碰撞检测其实不够完美,从视觉上看,明明已经到了边界,但是没有及时反弹。
修正一下:
import pygame
import sys pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BLACK = 0, 0, 0
RED = 255, 0, 0
SPEED = [1, 1]
angle = 1 screen = pygame.display.set_mode(SIZE)
originLeaf = pygame.image.load("leaf.png")
originRect = originLeaf.get_rect() clock = pygame.time.Clock() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() # 利用矩形对象的move方法,让rect移动
originRect = originRect.move(SPEED[0], SPEED[1])
# 注意:这里一定要用一个新变量newLeaf,保存旋转后的image对象
newLeaf = pygame.transform.rotate(originLeaf, angle)
angle += 1 # 注意:这里要定义一个新rect对象,因为图象旋转后,其外切的矩形尺寸会变化
newRect = newLeaf.get_rect()
# 默认的newRect位置在(0,0),要实现矩形外框跟随,必须赋值到新位置
newRect.left, newRect.top = originRect.left, originRect.top # 左右边界反弹的处理
if newRect.left <= 0 or newRect.right >= WIDTH:
SPEED[0] = -SPEED[0]
# 图片移动到接近右边界时(比如:right=198),由于旋转的作用,可能导致叶子一下横过来了,
# right突然会变成210,这样就算速度取反了,由于SPEED[0]=-1,需要10帧后,才能从视觉上真正看到反弹成功(即:210减到200,需要10次)
if newRect.right > WIDTH:
originRect.left = WIDTH - newRect.width # 上下边界反弹的处理
if newRect.top <= 0 or newRect.bottom >= HEIGHT:
SPEED[1] = -SPEED[1]
# 类似右边界的校正处理,防止叶子接近下边界时,由于旋转,一下从横到竖,高度突然加大,导致越界
if newRect.bottom > HEIGHT:
originRect.top = HEIGHT - newRect.height # 默认背景为白色,所以每渲染一帧,要对背景重新填充,否则会有上一帧的残影
screen.fill(BLACK)
# 画新矩形
pygame.draw.rect(screen, RED, newRect, 1)
# 将旋转后的图象,渲染到新矩形里
screen.blit(newLeaf, originRect)
# 正式渲染
pygame.display.update()
# 控制帧数<=100
clock.tick(100)
看上去好多了
最后再加点料,根据叶子的运动情况,动态调整背景颜色:
import pygame
import sys
import random
import math pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BACKGROUND_COLOR = (0, 0, 0) screen = pygame.display.set_mode(SIZE)
leaves = [] class Leaf(object):
def __init__(self, pos=[10, 10], speed=[1, 1]):
self.imageSrc = pygame.image.load("leaf.png")
self.rect = self.imageSrc.get_rect()
self.image = self.imageSrc
self.speed = speed
self.angle = 0
self.pos = pos
self.rect = self.rect.move(pos[0], pos[1]) def move(self):
self.rect = self.rect.move(self.speed[0], self.speed[1])
new_rect = self.image.get_rect()
new_rect.left, new_rect.top = self.rect.left, self.rect.top
if new_rect.left < 0 or new_rect.right > WIDTH:
self.speed[0] = -self.speed[0]
if new_rect.right > WIDTH:
self.rect.left = WIDTH - new_rect.width
if new_rect.left < 0:
self.rect.left = 0
if new_rect.top < 0 or new_rect.bottom > HEIGHT:
self.speed[1] = -self.speed[1]
if new_rect.bottom > HEIGHT:
self.rect.top = HEIGHT - new_rect.height def draw(self):
screen.blit(self.image, self.rect) def rotate(self):
self.image = pygame.transform.rotate(self.imageSrc, self.angle)
self.angle += random.randint(1, 5)
if math.fabs(self.angle) == 360:
self.angle = 0 def init():
for i in range(0, 3):
leaf = Leaf([random.randint(50, WIDTH - 50), random.randint(30, HEIGHT - 200)],
[random.randint(1, 2), random.randint(1, 2)])
leaf.move()
leaves.append(leaf) def to255(x):
if x > 1:
x = 1
return int(255 * math.fabs(x)) clock = pygame.time.Clock()
init() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() first_rect = leaves[0].rect # 根据第1片叶子的运动情况,随机切换背景色
color_r = to255(first_rect.top / HEIGHT)
color_g = to255(first_rect.left / WIDTH)
color_b = to255(math.fabs(first_rect.left) / (math.fabs(first_rect.top) + math.fabs(first_rect.left) + 1))
# print(color_r, color_g, color_b)
screen.fill((color_r, color_g, color_b)) # 将旋转后的图象,渲染到新矩形里
for item in leaves:
item.rotate()
item.move()
item.draw() # 正式渲染
pygame.display.update()
# 控制帧数<=100
clock.tick(100)
下边界检测时,还能做些变化,比如:叶子落到地面以下,就重新放回顶端,这样就有漫天落叶,绵绵不断的感觉:
import pygame
import sys
import random
import math pygame.init() SIZE = WIDTH, HEIGHT = 200, 400
BACKGROUND_COLOR = (230, 255, 230) screen = pygame.display.set_mode(SIZE)
leaves = [] class Leaf(object):
def __init__(self, pos=[10, 10], speed=[1, 1]):
self.imageSrc = pygame.image.load("leaf.png")
self.rect = self.imageSrc.get_rect()
self.image = self.imageSrc
self.speed = speed
self.angle = 0
self.pos = pos
self.rect = self.rect.move(pos[0], pos[1]) def move(self):
self.rect = self.rect.move(self.speed[0], self.speed[1])
new_rect = self.image.get_rect()
new_rect.left, new_rect.top = self.rect.left, self.rect.top
if new_rect.left < 0 or new_rect.right > WIDTH:
self.speed[0] = -self.speed[0]
if new_rect.right > WIDTH:
self.rect.left = WIDTH - new_rect.width
if new_rect.left < 0:
self.rect.left = 0
if new_rect.top > HEIGHT:
self.rect.bottom = 0 def draw(self):
screen.blit(self.image, self.rect) def rotate(self):
self.image = pygame.transform.rotate(self.imageSrc, self.angle)
self.angle += random.randint(1, 5)
if math.fabs(self.angle) == 360:
self.angle = 0 def init():
for i in range(0, 5):
leaf = Leaf([random.randint(50, WIDTH - 50), random.randint(30, HEIGHT)],
[random.randint(1, 2), random.randint(1, 2)])
leaf.move()
leaves.append(leaf) clock = pygame.time.Clock()
init() while True: for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() screen.fill(BACKGROUND_COLOR) # 将旋转后的图象,渲染到新矩形里
for item in leaves:
item.rotate()
item.move()
item.draw() # 正式渲染
pygame.display.update()
# 控制帧数<=100
clock.tick(100)
参考:https://www.pygame.org/wiki/RotateCenter?parent=CookBook
pygame 笔记-9 图片旋转及边界反弹的更多相关文章
-
自己积累的一些Emgu CV代码(主要有图片格式转换,图片裁剪,图片翻转,图片旋转和图片平移等功能)
using System; using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; na ...
-
PHPThumb处理图片,生成缩略图,图片尺寸调整,图片截取,图片加水印,图片旋转
[强烈推荐]下载地址(github.com/masterexploder/PHPThumb). 注意这个类库有一个重名的叫phpThumb,只是大小写的差别,所以查找文档的时候千万注意. 在网站建设过 ...
-
js无刷新上传图片,服务端有生成缩略图,剪切图片,iphone图片旋转判断功能
html: <form action="<{:AppLink('circle/uploadimg')}>" id="imageform" me ...
-
jQuery图片旋转展示收缩效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
-
iOS_UIImage_图片旋转
一.目的: 有时候我们获得到的图片我们不是我们想要的方向,需要对图片进行旋转.比如:图片旋转90度180度等. 二.实现过程. 1.获取到该UIImage. 2.开启上下文. 3.上下文的具体操作. ...
-
Rotating Image Slider - 图片旋转切换特效
非常炫的图片旋转滑动特效,相信会给你留下深刻印象.滑动图像时,我们会稍稍旋转它们并延缓各元素的滑动.滑块的不寻常的形状是由一些预先放置的元素和使用边框创建.另外支持自动播放选项,鼠标滚轮的功能. 在线 ...
-
JQuery插件让图片旋转任意角度且代码极其简单
引入下方的jquery.rotate.js文件,然后通过$("选择器").rotate(角度);可以旋转任意角度, 例如$("#rotate-image").r ...
-
HTML5图片旋转
HTML5图片旋转 首先我们使用Cococs2dx-Js-Lite版,来创建一个工程,我们所需要的开发环境如下: 1,webstrom 2,google chrome浏览器 3,cocos2dx-Js ...
-
Android提高篇之自定义dialog实现processDialog“正在加载”效果、使用Animation实现图片旋转
知识点: 1.使用imageview.textview自定义dialog 2.使用Animation实现图片旋转动画效果 3.通过自定义theme去掉dialog的title 没有使用progres ...
随机推荐
-
Cool!15个超炫的 CSS3 文本特效【上篇】
每一个网页设计师都希望创建出让用户能够赏识的网站.当然,这是不可能满足每个人的口味的.幸运的是,我们有最强大的工具和资源.实际上,我们非常多的网站模板,框架,内容管理系统,先进的工具和其他的资源可以使 ...
-
.NET和java的RSA互通,仅此而已
.NET和java的RSA互通,仅此而已 在开始这篇文章之前,先请读者朋友阅读老唐的这两篇文章: 1.Java与.Net环境下RSA加密解密交互不成功的问题解决 2.Java与.Net环境下RSA加密 ...
-
4.VUEX到底是什么
关于vuex类的新闻最近很多,看到眼热就去查了下资料,然后扯出来一堆flux.redux.state.state之类的概念,以及大型工程必要性之类的.看官方手册也是昏昏然. 然而,我还是弄懂了!我准备 ...
-
用Webstrom搭建Vue项目
一.首先要有Node.js Webpack环境 1.Node.js:是一个能够在服务器端运行JavaScript的开放源代码,跨平台JavaScript运行环境.Node采用Google开发的V8 ...
-
安装pygame出现is not a supported wheel on this platform解决办法
安装python库pygame时出现如下错误: 查看python的版本是否与之匹配,发现版本不匹配问题 例如1.我的python3.6是32位的,就只能安装cp36的:结果发现安装还是出现问题: 2. ...
-
第二十八篇-Fragment静态用法
效果图: 首先,先大致布局成这个形状 看动画中,横看分为两个区域,所以整体是一个水平排列 设置外层LinearLayout的参数 android:orientation="horizonta ...
-
python函数(一)
python函数(一) 1.函数的定义: def test(): print('test is running...') return 定义一个函数,有3个部分需要注意: 函数名称.函数的命名规范与变 ...
-
MySQL数据库索引之B+树
一.B+树是什么 B+ 树是一种树型数据结构,通常用于数据库和操作系统的文件系统中.B+ 树的特点是能够保持数据稳定有序,其插入与修改操作拥有较稳定的对数时间复杂度.B+ 树元素自底向上插入,这与二叉 ...
-
关于Zookeeper选举机制
zookeeper集群 配置多个实例共同构成一个集群对外提供服务以达到水平扩展的目的,每个服务器上的数据是相同的,每一个服务器均可以对外提供读和写的服务,这点和redis是相同的,即对客户端来讲每个服 ...
-
Vue 基本指令和html常用标签结合使用综合案例(含代码)
最近项目中要开发一个OA审批:里边涉及到流程跳转(流程较多),具体方案有:直接下一步,选择参与人或者选择某一个流程之后再选择参与人: 我们前端是APiCloud开发,这里我主要使用Vue来实现,把实现 ...