Python游戏引擎开发(六):动画的小小研究
今天我們來研究動畫,其實這個動畫就是一個Sprite+Bitmap的結合體。不造什么是Sprite和Bitmap?=__=#看來你是半路殺進來的,快去看看前幾章吧:
Python游戲引擎開發(一):序
Python游戲引擎開發(二):創建窗口以及重繪界面
Python游戲引擎開發(三):顯示圖片
Python游戲引擎開發(四):TextField文本類
Python游戲引擎開發(五):Sprite精靈類和鼠標事件
動畫的原理
一般而言,我們的動畫是用的這樣一種圖片:
播放動畫的時候,像播放電影一樣,這張圖就是膠卷。我們可以弄一個放映機,放映機的鏡頭大小就是每個動作小圖的大小。如果我們的膠卷不停地移動,那么就會連成動畫,如下圖:
如何實現這個效果呢?我們在第三章中學到了如何顯示圖片,其中提到了BitmapData類(不懂?良辰勸你去讀讀前幾章),這個類中有個兩個方法:setCoordinate和setProperty用于設置圖片顯示的位置和大小:
bmpd.setCoordinate(x, y) bmpd.setProperty(x, y, width, height)參數圖解如下:
在播放動畫時,我們的“膠卷”就是一個Bitmap圖片顯示對象,其中包含了一個BitmapData對象,我們通過調用這個對象的上述兩個方法,就能實現動畫播放。
不過到此好像還是少了什么?也許你會問,動畫是個連續的過程,且每幀動畫之間需要間隔一點時間,是不是少了一個計時器?是的,是的,是的,重要的事情說三遍,我們的確少了一個計時器類似物。不過別急。大家還記得第三章中提到的顯示對象的_show方法嗎?這個方法是在窗口的paintEvent中被調用的,paintEvent又是在一個計時器中被調用的(涉及第二章內容)。等等……計時器……所以我們其實已經有計時器了,差了個進入計時器的接口罷了。
時間軸事件
既然少了個接口,那么加個不就完了嘛。更改DisplayObject的_show方法:
def _show(self, c):if not self.visible:return# 加入時間軸事件入口self._loopFrame()c.save()c.translate(self.x, self.y)c.setOpacity(self.alpha * c.opacity())c.rotate(self.rotation)c.scale(self.scaleX, self.scaleY)self._loopDraw(c)c.restore()就加入了時間軸事件入口那一行代碼。這個方法在子類Sprite中具體實現:
def _loopFrame(self):e = object()e.currentTarget = selfs.__enterFrameListener(e)其中__enterFrameListener為新加入Sprite的屬性,是時間軸事件的監聽器。與鼠標事件相同,我們向監聽器傳入一個參數,用于獲取事件信息。
更改Sprite的addEventListener使其能加入時間軸事件:
def addEventListener(self, eventType, listener):if eventType == Event.ENTER_FRAME:self.__enterFrameListener = listenerelse:self.mouseList.append({"eventType" : eventType,"listener" : listener})再在Event類中定義一下時間軸事件ENTER_FRAME:
class Event(Object):ENTER_FRAME = "enter_frame"# more code...使用時,這么寫就OK:
layer = Sprite() layer.addEventListener(Event.ENTER_FRAME, onframe)def onframe(e):print("enter frame")簡單的動畫類
動畫類:
class Animation(Sprite):def __init__(self, bitmapData = BitmapData(), frameList = [[AnimationFrame()]]):super(Animation, self).__init__()這個類需要一個BitmapData對象作為參數,還需要一個list對象,這個list是用來裝AnimationFrame對象的幀列表,AnimationFrame顧名思義是一個保存每幀數據的類。代碼實現如下:
class AnimationFrame(object):def __init__(self, x = 0, y = 0, width = 0, height = 0):super(AnimationFrame, self).__init__()self.x = xself.y = yself.width = widthself.height = height其中x,y屬性儲存了每幀位于圖片上的位置,width和height儲存每幀的寬高。
對于一般的動畫圖片(上文中的示例圖片),每幀都是均勻分布在圖片上的,所以我們可以加入一個函數進行幀的均勻裁剪,這樣一來,我們獲取幀列表就會方便很多。為Animation類添加如下代碼:
def divideUniformSizeFrames(width = 0, height = 0, col = 1, row = 1):result = []frameWidth = width / colframeHeight = height / rowfor i in range(row):rowList = []for j in range(col):frame = AnimationFrame(j * frameWidth, i * frameHeight, frameWidth, frameHeight)rowList.append(frame)result.append(rowList)return result接下來,我們調用這個函數,傳入相應參數就可以切割出幀列表了。如下:
l = Animation.divideUniformSizeFrames(160, 160, 4, 4)# 得到如下列表: [ [AnimationFrame(0, 0, 40, 40), AnimationFrame(40, 0, 40, 40), AnimationFrame(80, 0, 40, 40), AnimationFrame(120, 0, 40, 40)], [AnimationFrame(0, 40, 40, 40), AnimationFrame(40, 40, 40, 40), AnimationFrame(80, 40, 40, 40), AnimationFrame(120, 40, 40, 40)], [AnimationFrame(0, 80, 40, 40), AnimationFrame(40, 80, 40, 40), AnimationFrame(80, 80, 40, 40), AnimationFrame(120, 80, 40, 40)], [AnimationFrame(0, 120, 40, 40), AnimationFrame(40, 120, 40, 40), AnimationFrame(80, 120, 40, 40), AnimationFrame(120, 120, 40, 40)] ]接下來就是實現播放動畫了,修改Animation類:
class Animation(Sprite):def __init__(self, bitmapData = BitmapData(), frameList = [[AnimationFrame()]]):super(Animation, self).__init__()self.bitmapData = bitmapDataself.frameList = frameListself.bitmap = Bitmap(bitmapData)self.currentRow = 0self.currentColumn = 0self.addEventListener(Event.ENTER_FRAME, self.__onFrame)def __onFrame(self, e):currentFrame = self.frameList[self.currentRow][self.currentColumn]self.bitmap.bitmapData.setProperty(currentFrame.x, currentFrame.y, currentFrame.width, currentFrame.height)self.currentColumn += 1if self.currentColumn >= len(self.frameList[self.currentRow]):self.currentColumn = 0由于這個類繼承自Sprite,所以就繼承了加入事件的addEventListener方法。以上代碼實現的是播放一排動畫,大家可以自行拓展為播放一列動畫或者播放整組動畫。
這樣一來,寫入以下代碼就能播放動畫了:
# 加載圖片 loader = Loader() loader.load("./player.png")# 動畫數據 bmpd = BitmapData(loader.content) l = Animation.divideUniformSizeFrames(160, 160, 4, 4)# 加入動畫 anim = Animation(bmpd, l) addChild(anim)預告:下一篇我們來繪制矢量圖形。
歡迎大家繼續關注我的博客
轉載請注明出處:Yorhom’s Game Box
http://blog.csdn.net/yorhomwang
總結
以上是生活随笔為你收集整理的Python游戏引擎开发(六):动画的小小研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 18 freertos消息队列-任务通信
- 下一篇: python怎么创建txt文件啊_搞定P