第 6章?動(dòng)畫與音效

本章內(nèi)容:通過(guò)之前幾章的學(xué)習(xí)臊恋,我們學(xué)到了大多數(shù)游戲所需的東西,你現(xiàn)在完全可以做一些小游戲了墓捻。比如FC游戲小蜜蜂(Galaxian)抖仅。或者坦克大戰(zhàn)等等砖第。但是撤卢,現(xiàn)在還不太好玩,因?yàn)楫嬅姹容^生硬梧兼,而且沒有聲音放吩,現(xiàn)在,我們就開始介紹如何做出動(dòng)畫效果及播放聲音羽杰。

[TOC]

動(dòng)畫與音效

動(dòng)畫

動(dòng)畫能夠很好的提高畫面的可視性渡紫,當(dāng)然到推,動(dòng)畫不單單是一個(gè)單純的貼圖替換的過(guò)程,也可能包含圖片顏色的改變惕澎,位置的改變莉测,形狀的改變等等。就平時(shí)常用的而言唧喉,動(dòng)畫分為下面幾種:幀動(dòng)畫捣卤,骨骼動(dòng)畫,粒子效果八孝,mesh變形等等腌零。他們并沒有一個(gè)完全嚴(yán)格意義的概念劃分,只是我們?cè)谑褂脮r(shí)的人為劃分唆阿。

幀動(dòng)畫

幀動(dòng)畫是2d游戲中最常用的動(dòng)畫形式益涧,因?yàn)樗?jiǎn)單易用,基本不涉及什么計(jì)算驯鳖,畫面表現(xiàn)豐富闲询。當(dāng)然,它也有相應(yīng)的缺點(diǎn)浅辙,比如貼圖占據(jù)空間大扭弧,越流暢、精細(xì)的畫面记舆,占據(jù)內(nèi)存空間越大鸽捻,繪制比較麻煩,因?yàn)槊恳粠瑤缀醯扔谥乩L泽腮。
幀動(dòng)畫還有一種變體御蒲,是將動(dòng)畫的各個(gè)部分劃分為小的動(dòng)畫,比如頭部诊赊,手臂等厚满,然后把他們通過(guò)固定的位置拼接在一起。這種好處是可以替換某個(gè)部分來(lái)實(shí)現(xiàn)換裝備碧磅,換武器或者一個(gè)類型的動(dòng)畫可以組裝多種人物等等碘箍。

骨骼動(dòng)畫

骨骼動(dòng)畫是比較復(fù)雜的動(dòng)畫,但是它有很多優(yōu)點(diǎn)鲸郊,比如可以復(fù)用貼圖丰榴,不用畫很多幀,方便控制秆撮,可以結(jié)合mesh動(dòng)畫等四濒。目前比較流行的有spine和dragonbone兩個(gè)骨骼動(dòng)畫編輯器。其中spine有對(duì)應(yīng)love2d的運(yùn)行時(shí),所以一般使用骨骼動(dòng)畫都用spine峻黍,不過(guò)spine本身是收費(fèi)的,而且不便宜拨匆。骨骼動(dòng)畫的原理是將一個(gè)人物分為若干塊骨頭姆涩,我們主要控制骨頭的長(zhǎng)短,角度惭每,位置等骨饿,然后骨頭上綁定貼圖,就可以了台腥,所以最簡(jiǎn)單的骨骼動(dòng)畫記錄的是隨時(shí)間變化各個(gè)骨骼的位置和角度宏赘。
剛剛提到的幀動(dòng)畫變體實(shí)際上有時(shí)也可以加入骨骼動(dòng)畫,只不過(guò)一般不太設(shè)計(jì)關(guān)節(jié)角度的繼承關(guān)系黎侈,而是直接用絕對(duì)位置和絕對(duì)角度察署。因?yàn)樗麄兊木庉嫳容^復(fù)雜,或者沒有一個(gè)統(tǒng)一的定式峻汉,這里不多講贴汪,后面可以針對(duì)性的自己試著實(shí)現(xiàn)。

粒子效果

實(shí)際上熬甫,粒子效果本身不太算動(dòng)畫部分漆弄,但是因?yàn)樗麄円采婕百N圖恩溅,位置,角度阳懂,大小等的變化,所以放在這里將柜思。粒子效果實(shí)際上是一大批的小圖片岩调,按照預(yù)先設(shè)計(jì)好的存在時(shí)間,位置赡盘,速度誊辉,角度,加速度亡脑,顏色等等(很多有隨機(jī)成分)堕澄,加入到場(chǎng)景,并當(dāng)存在時(shí)間到時(shí)時(shí)自動(dòng)消失霉咨。我們常用見的比如蛙紫,魔法效果,火星途戒,灰塵坑傅,閃光,火焰等喷斋,均可由粒子效果來(lái)模擬唁毒。對(duì)了蒜茴,加一句計(jì)算機(jī)圖形的名言“這個(gè)東西看起來(lái)是真的,那它就是真的”浆西。

mesh動(dòng)畫

也可以叫做mesh的動(dòng)態(tài)形變粉私。如果知道些繪圖原理或者3d渲染的同學(xué),應(yīng)該理解什么是mesh近零,它是計(jì)算機(jī)渲染圖片的形式诺核,計(jì)算機(jī)會(huì)把欲繪制的位置與紋理的位置做一個(gè)對(duì)應(yīng)關(guān)系,一般至少要3個(gè)點(diǎn)來(lái)完成久信。一個(gè)方形的貼圖一般需要4個(gè)點(diǎn)窖杀,即兩個(gè)三角形來(lái)完成渲染。而mesh動(dòng)畫則是把一個(gè)貼圖布滿mesh網(wǎng)格裙士,每個(gè)單元都是三角形入客。然后通過(guò)控制這些單元的頂點(diǎn)位置來(lái)達(dá)到變形效果。這種變形往往比較有張力腿椎,而且立體痊项。比如一個(gè)軟球,一個(gè)飄動(dòng)的旗子酥诽,一些透視形變等等鞍泉。這種效果一般也會(huì)結(jié)合骨骼動(dòng)畫來(lái)實(shí)現(xiàn),比如spine肮帐。所以有時(shí)咖驮,spine能做出以假亂真的3d效果。

本章僅具體介紹一下幀動(dòng)畫的原理和庫(kù)训枢,骨骼動(dòng)畫可以到spine的運(yùn)行時(shí)的github上找到案例(去找一個(gè)叫l(wèi)ove2d的branch)托修,粒子效果可以找一些粒子工具來(lái)體驗(yàn)一下感覺,其實(shí)所有的參數(shù)都是通用的恒界。mesh動(dòng)畫就還是找spine運(yùn)行時(shí)睦刃。

幀動(dòng)畫原理及實(shí)現(xiàn)。

基本原理

幀動(dòng)畫的基本遠(yuǎn)離實(shí)際上跟電影膠片差不多十酣,就是利用人的視覺殘像原理涩拙,以及人腦自動(dòng)補(bǔ)全的原理的,通過(guò)快速的切換一些畫面的顯示來(lái)達(dá)到某個(gè)圖片看起來(lái)像在動(dòng)一樣耸采。
幀動(dòng)畫一般涉及幾個(gè)要素兴泥,一個(gè)是幀序列,就是一組圖片虾宇。一個(gè)是延遲時(shí)間搓彻,幀與幀之間的延遲間隔。還有一個(gè)是循環(huán)模式,比如單次旭贬,順序循環(huán)怔接,乒乓循環(huán)幾種。

多圖幀動(dòng)畫

多圖是比較簡(jiǎn)單的方式稀轨,每個(gè)圖片是已經(jīng)切割好的紋理扼脐,我們按正常的方法建立一組image對(duì)象,然后通過(guò)一個(gè)timer來(lái)控制播放即可靶端。下面通過(guò)代碼演示:

local images = {}
local cd = 1/20
local timer = cd
local index = 1
for i = 1, 10 do
    images[i] =  love.graphics.newImage("res/image"..i..".png")
end
function Animation_update(dt)
    timer = timer - dt
    if timer<0 then
        timer = cd
        index = index + 1 
        if index>#images then
            index = 1
        end
    end
end
function Animation_draw()
    love.graphics.draw(images[index])
end

上面的代碼比較簡(jiǎn)單谎势,唯一需要注意的是引入image對(duì)象時(shí)凛膏,對(duì)文件名的處理杨名,可以用string.format進(jìn)行格式化。

spritesheet幀動(dòng)畫

精靈表幀動(dòng)畫的原理實(shí)際跟上面是沒有區(qū)別的猖毫,只是由于如果圖片不是二次冪(自己百度)的話台谍,它實(shí)際占用的顯存要高于圖片本身。所以一般把圖片整理后放到一個(gè)大圖片中吁断,這個(gè)圖片叫做精靈清單趁蕊,壓制這種圖片的工具比較多,比較著名的是spritepacker仔役,額掷伙,也是個(gè)收費(fèi)工具。在love論壇中有對(duì)其文件格式解讀的工具又兵,由于筆者并不用這個(gè)軟件任柜,所以請(qǐng)自行查閱。另外沛厨,在網(wǎng)上還有很多并沒有給出具體拆分方式的精靈清單宙地。如果他們的間隔是固定的,(比如一些人做的是64*64大小逆皮,每個(gè)精靈之間再額外間隔2個(gè)像素),這種就比較容易處理宅粥。不過(guò)有素材,可能是從某些游戲?qū)С龅牡缫ィ臼谴嬖谝粋€(gè)清單的秽梅,但是導(dǎo)出時(shí)沒能找到,這種情況就需要手動(dòng)切圖剿牺,在重新排布了风纠。常用的有比如photoshop,graphicgale等。
從代碼實(shí)現(xiàn)上牢贸,與上面的代碼的區(qū)別在于需要引入love的另一個(gè)對(duì)象叫做quad竹观。它可以用來(lái)作為draw的第二個(gè)參數(shù),從而告訴draw函數(shù)要繪制目標(biāo)的哪個(gè)矩形區(qū)域。
quad在定義時(shí)臭增,需要x,y,w,h參數(shù)懂酱,另外還需要一個(gè)圖片整體大小。需要說(shuō)明的是誊抛,像素的起點(diǎn)是0列牺,0 而非1,1 所以寬100拗窃,高100的圖片瞎领,實(shí)際x的取值范圍是0,99。
所以有下面代碼:

local image = love.graphics.newImage("res/imagesheet.png")
local imageW,imageH = image:getDimensions()
local cd = 1/20
local timer = cd
local index = 1
local frames = {}
for i = 1, 10 do
    frames[i] =  love.graphics.newQuad((i-1)*64-1,0,64,64,imageW,imageH)
end
function Animation_update(dt)
    timer = timer - dt
    if timer<0 then
        timer = cd
        index = index + 1 
        if index>#frames then
            index = 1
        end
    end
end
function Animation_draw()
    love.graphics.draw(image,frames[index])
end

幀動(dòng)畫庫(kù)

常用的幀動(dòng)畫庫(kù)anim8,它建立動(dòng)畫分為兩個(gè)步驟随夸,首先建立一個(gè)網(wǎng)格九默,然后通過(guò)網(wǎng)格建立動(dòng)畫,具體用法可以自行g(shù)ithub宾毒。

local anim8 = require 'anim8'
local image, animation
function love.load()
  image = love.graphics.newImage('path/to/image.png')
  local g = anim8.newGrid(32, 32, image:getWidth(), image:getHeight())
  animation = anim8.newAnimation(g('1-8',1), 0.1)
end
function love.update(dt)
  animation:update(dt)
end
function love.draw()
  animation:draw(image, 100, 200)
end

另外驼修,筆者也自己寫了一個(gè)簡(jiǎn)單的animation庫(kù),可以在筆者github上找到诈铛。

Animation = require "animation"
local anim = Animation:new(img,fx,fy,w,h,offx,offy,lx,ly,delay,count)
function love.update(dt)
    anim:update(dt)
end
function love.draw()
    anim:draw()
end

其中參數(shù)img為圖片對(duì)象乙各,fx,fy為含有動(dòng)畫的第一幀左上角位置,w,h為每個(gè)幀的尺寸幢竹,offx,offy為幀之間的間隔耳峦,lx,ly為最后一幀右下角的位置,delay為幀延遲時(shí)間焕毫,count為取這個(gè)動(dòng)畫中的前多少幀(可以缺省為最多)蹲坷。這個(gè)庫(kù)包含一些基本的回調(diào)。因?yàn)楹芎?jiǎn)單咬荷,所以自己看源碼就看得懂冠句。

聲音

在love2d里,聲音對(duì)象是由love.audio.newSource來(lái)導(dǎo)入的幸乒,因?yàn)楹芎?jiǎn)單下面僅舉個(gè)例子懦底。

local music = love.audio.newSource("love.mp3")
music:play()
-- love.audio.play(music)

有兩點(diǎn)要說(shuō)明,newSource的參數(shù)的第二個(gè)為類型罕扎,有兩種聚唐,一種是靜態(tài)一種是流式,前者比較快腔召,但占內(nèi)存杆查,后者相反。缺省為流式臀蛛。如果像類似機(jī)槍那種連續(xù)的播放某個(gè)聲音的話亲桦,需要建立多個(gè)聲音對(duì)象崖蜜,如果僅一個(gè)的話,只能在這個(gè)聲音完全播完客峭,才能再次播放豫领。

編程時(shí)間

這次我們的坦克再次升級(jí)啦,改為飛機(jī)了舔琅,現(xiàn)在給坦克披上飛機(jī)的皮等恐,它就是飛機(jī)了,不過(guò)炮塔也沒有了备蚓,用鼠標(biāo)來(lái)操控飛機(jī)的方向课蔬。

設(shè)計(jì)階段

飛機(jī)能夠跟隨鼠標(biāo)的方向轉(zhuǎn)動(dòng),并自動(dòng)向前飛行郊尝,飛機(jī)有轉(zhuǎn)動(dòng)速度二跋。
飛機(jī)能夠按下按鍵時(shí)發(fā)射子彈。
飛機(jī)綁定一個(gè)動(dòng)畫虚循。
設(shè)計(jì)一個(gè)子彈類同欠,子彈在射出時(shí)固定角度样傍,按固定速度移動(dòng)横缔,離開圖像邊界子彈銷毀。
設(shè)計(jì)一個(gè)敵人飛機(jī)類衫哥,并綁定一個(gè)動(dòng)畫茎刚,敵人飛機(jī)自動(dòng)發(fā)射子彈,但敵人飛機(jī)的子彈速度要慢一點(diǎn)撤逢。
敵人的初始位置為以畫面為中心膛锭,半徑rx = 400,ry =300的隨機(jī)外圍位置蚊荣。并以畫面中心為目標(biāo)飛行初狰。
敵人飛機(jī)在超過(guò)邊界時(shí),銷毀并重新生成一個(gè)飛機(jī)互例。
暫時(shí)不涉及碰撞奢入。留作作業(yè)。
根據(jù)上述設(shè)計(jì)要求媳叨,我們首先確定了制作3個(gè)類腥光,一個(gè)玩家,一個(gè)敵人糊秆,一個(gè)子彈武福。動(dòng)畫部分我們準(zhǔn)備使用anim8庫(kù)。
玩家跟隨鼠標(biāo)的算法痘番,我們需要考慮下如何實(shí)現(xiàn)捉片,敵人生成的位置還是比較簡(jiǎn)單的平痰。

實(shí)現(xiàn)階段

  1. 三個(gè)類的基本框架,我們?cè)谏弦徽乱呀?jīng)講過(guò)了伍纫,這里不再贅述觉增。(實(shí)際上,敵人飛機(jī)雖然像飛機(jī)翻斟,但是它跟子彈在代碼上更加相似逾礁。所以,有時(shí)候?qū)τ谟螒騺?lái)講访惜,要從一個(gè)物體的行為本質(zhì)思考嘹履,而不是外面的皮膚是什么樣的)
  2. 飛機(jī)類跟隨鼠標(biāo)的算法,這里簡(jiǎn)單講解一下债热。先把代碼貼出來(lái)砾嫉,再簡(jiǎn)要的說(shuō)明:
local function getRot(x1,y1,x2,y2)
    if x1==x2 and y1==y2 then return 0 end 
    local angle=math.atan((x2-x1)/(y2-y1))
    if y1-y2<0 then angle=angle-math.pi end
    if angle>0 then angle=angle-2*math.pi end
    return -angle
end
local function unitAngle(angle)  --convert angle to 0,2*Pi
    angle = angle%(2*math.pi)
    if angle > math.pi then angle = angle - 2* math.pi end
    return angle
end
local tx,ty = love.mouse.getPosition()
local rot = unitAngle(getRot(self.x,self.y,tx,ty)) --這里是計(jì)算方位角的一種方法。
self.rot = unitAngle(self.rot)
if rot>self.rot and math.abs(rot - self.rot)< math.pi or
     rot< self.rot and  math.abs(rot - self.rot)> math.pi then
    self.rot = self.rot + self.dr
else
    self.rot = self.rot - self.dr
end

首先窒篱,獲取方位角的公式焕刮,上一章已經(jīng)講過(guò)了,數(shù)學(xué)方法不說(shuō)了墙杯,當(dāng)黑箱使用配并。返回的是兩點(diǎn)間方位角。
第二個(gè)函數(shù)是獲得單位角度高镐,因?yàn)槲覀兊慕嵌炔粩喁B加溉旋,可能不在-Pi~Pi(因?yàn)檫@個(gè)區(qū)間段比較容易跟0進(jìn)行比較)的范圍內(nèi),但是由于三角函數(shù)都是周期函數(shù)嫉髓,所以從外觀上沒有什么影響观腊,但是如果進(jìn)行加減或比較,就要出問題了算行,所以要進(jìn)行歸一化梧油。同樣,數(shù)學(xué)方法不再解釋了州邢。
然后我們到判斷轉(zhuǎn)向部分儡陨,self.dr是飛機(jī)的轉(zhuǎn)動(dòng)速度,首先我們得到鼠標(biāo)與飛機(jī)的方位角rot,然后跟飛機(jī)當(dāng)前的角度進(jìn)行比較偷霉,如果方位角大于飛機(jī)的角又小于半圈(math.pi)則右轉(zhuǎn)迄委,或者小于飛機(jī)角度,但大于半圈(這種情況實(shí)際是飛機(jī)處于正方向的兩端),否則左轉(zhuǎn)类少。(圖示我下次修訂教程的時(shí)候再補(bǔ)).

  1. 關(guān)于敵人飛機(jī)發(fā)射子彈以及子彈減速的問題叙身。
local b = Bullet(self)
b.vx = b.vx/3
b.vy = b.vy/3
table.insert(game.objects,b)

在敵人發(fā)射子彈時(shí),生成一個(gè)子彈實(shí)例硫狞,實(shí)際上跟玩家是一樣的信轿。要減速晃痴,則要改變子彈實(shí)例的速度(而非類的速度,否則所有子彈均被改變财忽,不能改變模板)倘核,這里注意的是,直接設(shè)置speed屬性是有問題的即彪,因?yàn)楹竺孀訌梪pdate里根本沒有涉及speed而是vx,vy,所以要改變它們紧唱,讓他們各自打3折即可。

  1. 敵人飛機(jī)生成位置
local rot = love.math.random()*2*math.pi
self.x = math.sin(rot)*400 + 400
self.y = -math.cos(rot)*300 + 300
self.rot = rot+math.pi

如何隨機(jī)的在一個(gè)圈的位置上生成位置隶校?同樣是數(shù)學(xué)問題漏益,不想多說(shuō)了,代碼在上面深胳,因?yàn)閞ot是指向外圈的绰疤,所以用rot+pi就是反向指向內(nèi)圈。(注意不是-rot舞终,-rot的意義是沿0對(duì)稱轻庆,而非反向,周期性不解釋)

  1. 關(guān)于anim8的用法
    請(qǐng)自己參閱anim8的文檔敛劝,這里不再贅述余爆。

  2. 動(dòng)態(tài)生成/刪除飛機(jī)
    我們之前說(shuō)過(guò)了,在某個(gè)對(duì)象遍歷過(guò)程中攘蔽,增添或刪除對(duì)象是十分危險(xiǎn)的龙屉,因?yàn)橛行虮砗芸赡芘判虮淮騺y呐粘。一般而言满俗,有兩種解決方案,我們?nèi)芜x作岖。當(dāng)然唆垃,如果你是無(wú)序表就無(wú)所謂了,但是也不能用insert或remove來(lái)控制痘儡,直接nil掉就可以了辕万。
    第一種方法,我叫做另起爐灶

new = {}
for i ,v in ipairs(objects)
    v:update() --table.insert(new,newObj)插入到new中沉删,而非當(dāng)前
    if not v.destroyed then table.insert(new,v) end
end
objects = new

第二種方法渐尿,是一種變通的方法。

for i = #objects,1 ,-1 do
    local go = objects[i]
    go:update(dt)  -- table.insert(objects,#objects,newObj) 倒序遍歷 加入時(shí)放到隊(duì)尾
    if go.destroyed then table.remove(objects,i) end
end

當(dāng)然矾瑰,還有一種方法就是不在遍歷中加入砖茸,而是放到后面。

if #objects<5 then --不過(guò)這種方法比較有局限性
    table.insert(objects,newObj)
end

上面介紹了本章節(jié)中比較復(fù)雜的代碼殴穴,其他的東西實(shí)際上是一樣的凉夯。只是代碼的量增加了货葬。

作業(yè)

  1. 增加些碰撞測(cè)試吧。讓敵人及其子彈對(duì)玩家有碰撞劲够,一旦碰撞就gameover了震桶。(使用bump庫(kù)很簡(jiǎn)單啦)
  2. 增添一些敵人的種類,子彈的方向等征绎。自定義速度蹲姐,子彈方向,及子彈速度人柿,顏色等淤堵。(需要稍微改一下bullet類,以便支持更多的自定義)
  3. 把這個(gè)游戲改編一下顷扩,變成常見的縱版彈幕的樣式拐邪,即飛機(jī)隨機(jī)從屏幕上方飛往下方,發(fā)射子彈隘截。玩家鼠標(biāo)控制飛機(jī)移動(dòng)扎阶,方向永遠(yuǎn)對(duì)著屏幕上方。然后設(shè)計(jì)幾種不同的武器類型婶芭,比如橫向的东臀,散彈的,追蹤的(算法跟用鼠標(biāo)控制飛機(jī)方向類似)等等犀农。

本章代碼

--------------------import -------------------
class = require "assets/middleclass"
anim8 = require "assets/anim8"
---------------------objects-------------------
Bullet = class("bullet")
Bullet.fireCD = 0.1
Bullet.radius = 5
Bullet.speed = 20
function Bullet:init(parent,rot)
    self.parent = parent 
    self.rot = self.parent.rot
    self.x = self.parent.x + math.sin(self.rot)*self.parent.w
    self.y = self.parent.y - math.cos(self.rot)*self.parent.w
    self.vx = self.speed * math.sin(self.rot)
    self.vy = -self.speed * math.cos(self.rot)
    self.tag = "bullet"
end
function Bullet:update(dt)
    self.x = self.x + self.vx
    self.y = self.y + self.vy
    if self.x > 800 or self.x<0 or self.y<0 or self.y > 600 then --邊界判斷
        self.destroyed = true
    end
end
function Bullet:draw()
    love.graphics.setColor(255,255,0,255)
    love.graphics.circle("fill",self.x,self.y,self.radius)
end
local Plane = class("plane")
Plane.speed =3
Plane.size = 1
Plane.texture = love.graphics.newImage("assets/res/1945.png")
Plane.g64 = anim8.newGrid(64,64, 1024,768,  299,101,   2)
Plane.dr = 0.1
function Plane:init(x,y,rot)
    self.x = x
    self.y = y
    self.rot = rot
    self.fireCD = Bullet.fireCD
    self.fireTimer = self.fireCD
    self.anim = anim8.newAnimation(self.g64(1,'1-3'), 0.1)
    self.w = self.size * 32
end
local function getRot(x1,y1,x2,y2)
    if x1==x2 and y1==y2 then return 0 end 
    local angle=math.atan((x2-x1)/(y2-y1))
    if y1-y2<0 then angle=angle-math.pi end
    if angle>0 then angle=angle-2*math.pi end
    return -angle
end
local function unitAngle(angle)  --convert angle to 0,2*Pi
    angle = angle%(2*math.pi)
    if angle > math.pi then angle = angle - 2* math.pi end
    return angle
end
local function getLoopDist(p1,p2,loop)
    loop=loop or 2*math.pi
    local dist=math.abs(p1-p2)
    local dist2=loop-math.abs(p1-p2)
    if dist>dist2 then dist=dist2 end
    return dist
end
function Plane:update(dt)
    self.anim:update(dt)
    local tx,ty = love.mouse.getPosition()
    local rot = unitAngle(getRot(self.x,self.y,tx,ty)) --這里是計(jì)算方位角的一種方法惰赋。
    self.rot = unitAngle(self.rot)
    if rot>self.rot and math.abs(rot - self.rot)< math.pi or
         rot< self.rot and  math.abs(rot - self.rot)> math.pi then
        self.rot = self.rot + self.dr
    else
        self.rot = self.rot - self.dr
    end
    self.fireTimer = self.fireTimer - dt --這里的開火計(jì)時(shí)器是十分常用的一種方法,需要學(xué)會(huì)
    if love.mouse.isDown(1) and self.fireTimer < 0 then
        self.fireTimer = self.fireCD
        table.insert(game.objects,Bullet(self))
    end
    self.x = self.x + self.speed*math.sin(self.rot)
    self.y = self.y - self.speed*math.cos(self.rot)
end
function Plane:draw()
    love.graphics.setColor(255, 255, 255, 255)
    self.anim:draw(self.texture,self.x,self.y,self.rot,self.size,self.size,32,32)
end
local Enemy = class("Enemy")
Enemy.speed =3
Enemy.size = 1
Enemy.texture = Plane.texture
Enemy.g64 = Plane.g64
function Enemy:init()
    local rot = love.math.random()*2*math.pi
    self.x = math.sin(rot)*400 + 400
    self.y = math.cos(rot)*300 + 300
    self.rot = -rot
    self.fireCD = 0.5
    self.fireTimer = self.fireCD
    self.anim = anim8.newAnimation(self.g64('2-4',3), 0.1)
    self.w = self.size * 32
    self.tag = "enemy"
end
function Enemy:update(dt)
    self.x = self.x + self.speed*math.sin(self.rot)
    self.y = self.y - self.speed*math.cos(self.rot)
    self.fireTimer = self.fireTimer - dt --這里的開火計(jì)時(shí)器是十分常用的一種方法呵哨,需要學(xué)會(huì)
    if self.fireTimer < 0 then
        self.fireTimer = self.fireCD
        local b = Bullet(self)
        b.vx = b.vx/3
        b.vy = b.vy/3
        table.insert(game.objects,b)
    end
    if self.x > 800 or self.x<0 or self.y<0 or self.y > 600 then --邊界判斷
        self.destroyed = true
    end
end
function Enemy:draw()
    love.graphics.setColor(255, 255, 255, 255)
    self.anim:draw(self.texture,self.x,self.y,self.rot,self.size,self.size,32,32)
end
game = {}
function love.load()
    love.graphics.setBackgroundColor(100, 100, 200, 255)
    game.objects = {}
    game.enemies = {}
    game.plane = Plane(400,300,0)
    for i = 1, 5 do
        table.insert(game.enemies,Enemy())
    end
end
function love.update(dt)
    game.plane:update(dt)
    for i = #game.objects,1 ,-1 do
        local go = game.objects[i]
        go:update(dt)
        if go.destroyed then table.remove(game.objects,i) end
    end
    for i = #game.enemies,1 ,-1 do
        local go = game.enemies[i]
        go:update(dt)
        if go.destroyed then table.remove(game.enemies,i) end
    end
    if #game.enemies<5 then
        table.insert(game.enemies,Enemy())
    end
end
function love.draw()
    game.plane:draw()
    for i,v in ipairs(game.objects) do
        v:draw()
    end
    for i,v in ipairs(game.enemies) do
        v:draw()
    end
end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赁濒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子孟害,更是在濱河造成了極大的恐慌拒炎,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挨务,死亡現(xiàn)場(chǎng)離奇詭異击你,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)谎柄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門丁侄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人朝巫,你說(shuō)我怎么就攤上這事鸿摇。” “怎么了捍歪?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵户辱,是天一觀的道長(zhǎng)鸵钝。 經(jīng)常有香客問我,道長(zhǎng)庐镐,這世上最難降的妖魔是什么恩商? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮必逆,結(jié)果婚禮上怠堪,老公的妹妹穿的比我還像新娘。我一直安慰自己名眉,他們只是感情好粟矿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著损拢,像睡著了一般陌粹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上福压,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天掏秩,我揣著相機(jī)與錄音,去河邊找鬼荆姆。 笑死蒙幻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胆筒。 我是一名探鬼主播邮破,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仆救!你這毒婦竟也來(lái)了抒和?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤派桩,失蹤者是張志新(化名)和其女友劉穎构诚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铆惑,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年送膳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了员魏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叠聋,死狀恐怖撕阎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碌补,我是刑警寧澤虏束,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布棉饶,位于F島的核電站,受9級(jí)特大地震影響镇匀,放射性物質(zhì)發(fā)生泄漏照藻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一汗侵、第九天 我趴在偏房一處隱蔽的房頂上張望幸缕。 院中可真熱鬧,春花似錦晰韵、人聲如沸发乔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)栏尚。三九已至,卻和暖如春只恨,著一層夾襖步出監(jiān)牢的瞬間抵栈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工坤次, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留古劲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓缰猴,卻偏偏與公主長(zhǎng)得像产艾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滑绒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 模型(Mesh) 在Unity3D中使用三維模型,主要依靠Mesh Filter組件載入多邊形表面物體(polyg...
    shimmery閱讀 21,320評(píng)論 2 34
  • 一:什么是協(xié)同程序纵势?答:在主線程運(yùn)行時(shí)同時(shí)開啟另一段邏輯處理踱阿,來(lái)協(xié)助當(dāng)前程序的執(zhí)行。換句話說(shuō)钦铁,開啟協(xié)程就是開啟一個(gè)...
    CrixalisAs閱讀 2,062評(píng)論 1 7
  • 1软舌、委托是什么,事件是委托嗎? 它們有什么區(qū)別牛曹? C#中委托通常是指委托類型創(chuàng)建的對(duì)象佛点,它用于保存和調(diào)用同類型的方...
    SeriousWilson閱讀 2,228評(píng)論 0 1
  • 一. 簡(jiǎn)介 動(dòng)畫在2D游戲里用得十分廣泛, 根據(jù)這些動(dòng)畫的特點(diǎn),我們可以大概歸為3類 1. 粒子動(dòng)畫 這種動(dòng)畫是由...
    955a74228446閱讀 1,874評(píng)論 0 1
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇超营!感恩不離不棄鸳玩。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,559評(píng)論 0 11