4.3. 編程實現
(續上)
3. 實現射擊捕魚
在第 3 個階段,將按照“編程思路”中介紹的射擊捕魚的算法進行編程,實現讓玩家操控大炮射擊捕魚。在“bydr”項目目錄中,把version2.jl複製一份並命名爲version3.jl,然後在第2個版本的基礎上編寫第3個版本的代碼。
1) 創建3個角色
由於GameZero沒有精靈(sprite)的概念,所以我們將game_sprites.jl,改名爲game_actor.jl,並在其中分別創建BulletSprite()函數生成炮彈角色、NetSprite()函數生成漁網角色和CoinSprite()函數生成硬幣角色。
BulletSprite()函數代碼如下:
1 function BulletSprite(x,y) 2 #'''炮彈精靈''' 3 actor=Actor(bullet_img) 4 actor.pos=x,y 5 actor.speed = 300 6 actor.alpha=255 7 #self.visible = True 8 return actor 9 end
炮彈角色有兩個屬性:speed 和 visible,分別用於控制炮彈的移動速度和可見狀態。不過GameZero的Actor沒有visible屬性,但是提供了alpha屬性,這個屬性可以控制Actor的透明度,alpha的值爲0表示完全透明,255表示完全不透明,因此可以用這個屬性達到控制炮彈可見性的目的。
另外創建函數fire_move()法用於實現炮彈的移動,當炮彈移動到遊戲窗口的可見區域之外,就讓其消失。
1 function fire_move(bullet::Actor, dt) 2 #移動炮彈 3 if bullet.alpha==255 4 move(bullet,bullet.speed * dt) 5 if bullet.x < 0 || bullet.x > 1024 || bullet.y > 768 6 #self.visible = False 7 bullet.alpha=0 8 end 9 end 10 end
NetSprite()函數代碼如下:
1 function NetSprite(netPos) 2 #'''漁網精靈''' 3 actor=Actor(fishing_net_img,pos=netPos) 4 #self.set_sprite_center() 5 actor.size = 0 6 #actor.visible = True 7 actor.alpha=255 8 return actor 9 end
漁網角色兩個屬性:size 和 visible,分別用於控制漁網張開的大小和可見性。可見性魚炮彈類同。
另外,創建open()函數,用於實現讓漁網由小慢慢變大並消失的動態效果。
function open(net::Actor, dt) #打開漁網 if net.scale <= [1,1] net.size += 300 * dt net.scale = [net.size / 100,net.size / 100] else #net.visible = false net.alpha=0 end end
注意Actor的屬性scale,可對角色圖像進行縮放,它是個含有兩個元素的數組,分別爲橫向和縱向縮放比例,因此它可以非等比縮放。
CoinSprite()函數的代碼如下:
1 function CoinSprite(coinPos, score=0) 2 #'''硬幣精靈''' 3 #println(silver_coin_img[1]) 4 actor=Actor(silver_coin_img[1],pos=coinPos) 5 actor.score = score 6 actor.speed = 400 7 #actor.visible = True 8 actor.alpha=255 9 actor.frame=1 10 actor.set_animation=coin_animation(score,actor) 11 #面向窗口底部的硬幣箱 12 point(actor,160, 597) 13 return actor 14 end
硬幣角色有3個屬性,Score用於記錄捕獲一條魚的得分,speed和visible分別用於控制硬幣移動速度和可見狀態。類中的 set_animation屬性用於按得分創建銀幣或金幣的動畫圖像。
這裏就顯現出Julia的獨特之處了!在Julia中處處皆對象。函數也是對象,因此可以將一個函數作爲某個對象的屬性。這裏,硬幣的set_animation屬性就被賦予了一個函數
actor.set_animation=coin_animation(score,actor)
而coin_animation()函數是這樣的:
function coin_animation(score,coin::Actor) #'''設置硬幣動畫''' images = (score <= 20) ? silver_coin_img : gold_coin_img function _animation() _frame=coin.frame if _frame < length(images) coin.image = images[_frame] _frame += 1 else _frame = 1 end coin.frame=_frame schedule_once(_animation, 1/16) end return _animation end
另外,創建move_down()函數,控制硬幣的移動,當硬幣移動到計分牌下方時,消失。
function move_down(coin::Actor, dt) #'''移動硬幣''' if coin.y < 600 move(coin,coin.speed * dt) else #coin.visible = False #println(coin.alpha) coin.alpha=0 end end
2) 控制射擊捕魚動
切換到“version3.jl”源文件,創建fire_control()函數,用於實現發射炮彈捕魚,檢測炮彈與魚的碰撞,以及控制漁網張開、硬幣落下等。
function fire_control(dt)…end
從 bullets 列表讀取各個炮彈精靈實例,讓炮彈精靈移動,並與 fishes 列表中的每個魚角色實例進行碰撞檢測。當炮彈角色碰到魚角色時,就讓炮彈消失,並在魚的位置拋出一張漁網。此外,還從 bullets 列表中移除處於不可見狀態的炮彈角色實例。
1 #'''捕魚控制''' 2 #炮彈 3 for bullet in bullets 4 if bullet.alpha==255 5 #移動炮彈 6 fire_move(bullet,dt) 7 if bullet.y < 150 8 continue 9 end 10 #對每條有生命的魚進行碰撞檢測 11 for fish in fishes 12 if fish.life <= 0 13 continue 14 end 15 if collide(bullet,fish) 16 #命中魚減去生命值 17 fish.life -= 1 18 #讓炮彈消失 19 #bullet.visible = False 20 bullet.alpha=0 21 #拋撒漁網在炮彈處 22 throw_fishing_net(fish.pos) 23 end 24 end 25 else 26 #delete!(bullets,bullet) 27 #bullets.remove(bullet) 28 #bullet.delete() 29 filter!(e->e≠bullet,bullets) 30 end 31 end
從 nets 列表中讀取各個漁網角色實例,讓漁網逐漸張開。當漁網角色處於不可見狀態時,就將其從 nets 列表中移除。
#漁網 for net in nets if net.alpha==255 open(net,dt) else #delete!(nets,net) #nets.remove(net) #net.delete() filter!(e->e≠net,nets) end end
從 coins 列表中讀取各個硬幣角色實例,讓硬幣角色向着窗口左下角的硬幣盒處移動。 之後 ,將捕魚的得分累加到全局變量 Score 中 ,並將硬幣角色從 coins 列表中移除。
global score_label #硬幣 for coin in coins if coin.alpha==255 move_down(coin,dt) else #加分 global score score += coin.score score_label = TextActor(@sprintf("%06d",score),"Arial-Black",font_size=24, color=Int[255,255,255,255]) score_label.pos=(165,635) #score_label.text = '%06d' % score #delete!(coins,coin) #coins.remove(coin) #coin.delete() filter!(e->e≠coin,coins) end end
至此,函數 fire_contro1( )編寫完畢,在窗口update事件方法裏調用。
接下來,創建fire_bullet()、throw_fishing_net()和 release_coin()這3 個函數,分別用於實現發射炮彈、拋出漁網和釋放硬幣的功能。在這些函數中將會生成炮彈角色、漁網角色和硬幣角色的實例,將它們加人到對應的bullets、nets或coins列表中。
function fire_bullet() #'''發射炮彈''' #println(cannon.angle) bullet = BulletSprite(0,0) bullet.angle = cannon.angle bullet.x=cannon.x+ (cannon.w-bullet.w)/2 bullet.y=cannon.y+ (cannon.h-bullet.h)/2 push!(bullets,bullet) #bullets.append(bullet) end function throw_fishing_net(pos) #'''拋撒漁網''' net = NetSprite(pos) push!(nets,net) #nets.append(net) end function release_coin(pos, score) #'''釋放金幣''' coin = CoinSprite(pos, score) coin.set_animation() #coin_animation(coin,score) push!(coins,coin) #coins.append(coin) end
在遊戲窗口中按下鼠標左鍵時,將調用 fire_bullet() 函數發射炮彈。
function on_mouse_down(g::Game, pos, button) if button==MouseButtons.LEFT #println("在窗口'$pos'處按下左鍵") fire_bullet() end end
還要在遊戲窗口的事件draw()方法中調用 draw_actor()函數,將 nets、coins和bullets 這 3 個列表中的漁網、硬幣和炮彈角色實例的圖像繪製到窗口中。
function draw(g::Game)
clear()
draw_actor(fishes)
draw_actor(nets)
draw_actor(coins)
draw(panel)
draw(score_label)
draw_actor(bullets)
draw(cannon)
end
至此,這個遊戲程序的第3個版本完成。運行程序,就能讓玩家操控大炮發射炮彈捕魚了。趕快試試吧!
4. 播放背景音樂
看着賞心悅目的畫面,再配上悅耳動聽的音樂,將讓遊戲體驗更佳。這是第 4 個階段要實現的功能,請自行實現。