使用Ring编程语言为桌面和Android开发Stars Fighter游戏






2.71/5 (7投票s)
Stars Fighter 是一款使用 Ring 编程语言为桌面和 Android 开发的简单 2D 游戏。
引言
Ring 编程语言附带一个简单的 2D 游戏引擎(用 Ring 编写),用于为桌面和移动平台原型开发简单的 2D 游戏。在本文中,我们将创建 Starts Fighter 游戏,以此作为使用游戏引擎在短时间内创建游戏的简单示例。
背景
要理解本文,您需要了解通用编程基础以及简单的 2D 游戏开发背后的基本概念,例如(图像、精灵和碰撞检测)。此外,还需要对 Ring 编程语言有基本了解(或至少有使用类似动态脚本语言(如 Python 和 Ruby)的经验)。
游戏引擎使用 Allegro 游戏编程库进行桌面开发,并使用 LibSDL 库进行移动开发。您将编写一次代码,无需直接与这些库交互,因为引擎提供了一个简单的层,让您可以专注于游戏本身。
查看关于 Ring 的后续文章
(1) Ring 编程语言
(2) Ring 语言的语法灵活性
以及接下来的教程
(1) 入门
(2) 控制结构
(3) 函数
(4) 列表
(5) 类
(6) 函数式编程
(7) 声明式编程
使用代码
游戏开始时会显示一个屏幕,上面显示游戏标题和版本。
我们将有背景音乐,一旦用户按下空格键、用鼠标单击或触摸移动屏幕,游戏就会开始。
首先,我们将使用一个全局变量(oGameState)。我们将游戏状态存储在这个对象中。
要开始使用游戏引擎,我们将使用: Load "GameEngine.ring"
然后我们将定义主函数,创建游戏对象并启动 while 循环。
我们将使用以下类:
| sprite | : 创建新的 Sprite 对象并将其添加到游戏对象中。 | 
| 文本 | : 创建新的 Text 对象并将其添加到游戏对象中。 | 
| sound | : 创建新的 Sound 对象并将其添加到游戏对象中。 | 
oGameState = NULL
Load "gameengine.ring"
func main
    oGame = New Game
    while true
    oGameState = new GameState
    oGame {
        title = "Stars Fighter!"
        sprite
        {
            file = "images/menu1.jpg"
            x = 0 y=0 width=800 height = 600 scaled = true animate = false
            keypress = func ogame,oself,nKey {
                if nkey = key_esc or nKey = GE_AC_BACK
                    ogame.shutdown()
                but nKey = key_space
                    oGameState.startplay=true
                    ogame.shutdown=true
                ok
            }
            mouse = func ogame,oself,nType,aMouseList {
                if nType = GE_MOUSE_UP
                    oGameState.startplay=true
                    ogame.shutdown=true
                ok
            }
        }
        text {
            animate = false
            size = 35
            file = "fonts/pirulen.ttf"
            text = "Stars Fighter"
            x = 10    y=50
        }
        text {
            animate = false
            size = 25
            file = "fonts/pirulen.ttf"
            text = "Version 1.0"
            x = 80    y=100
        }
        text {
            animate = false
            size = 16
            file = "fonts/pirulen.ttf"
            text = "(C) 2016, Mahmoud Fayed"
            x = 45    y=140
        }
        text {
            animate = false
            size = 25
            file = "fonts/pirulen.ttf"
            text = "Press Space to start"
            x = 190    y=470
        }
        text {
            animate = false
            size = 20
            file = "fonts/pirulen.ttf"
            text = "Press Esc to Exit"
            x = 260    y=510
        }
        Sound {
            file = "sound/music1.wav"
        }
    }
    if oGameState.startplay
        oGame.refresh()
        playstart(oGame)
        oGame.refresh()
    ok
    end
Text 类需要文本位置(x,y)、字体文件名(*.ttf)和字体大小。
Sound 类需要声音文件名(*.wav)。
Sprite 类提供了更多属性。我们有 (x,y,width 和 height) 用于定位。
我们还有其他属性,例如:
| 图像 | : String 确定图像文件名。 | 
| 点 | : Number 确定对象的自动移动限制。 | 
| direction | : Number 确定移动方向。 | 
| nstep | : Number 确定移动过程中的增量/减量。 | 
| type | : Number 确定游戏中的对象类型(可选)。 | 
| transparent | : True/False 值决定图像是否透明。 | 
Sprite 类也通过继承获得以下属性:
| enabled | : True/False 决定对象的状态(激活/未激活)。 | 
| x | : Number 决定对象的 x 坐标。 | 
| 是 | : Number 决定对象的 y 坐标。 | 
| width | : Number 决定对象的宽度。 | 
| height | : Number 决定对象的高度。 | 
| nIndex | : Number 确定对象在对象列表中的索引。 | 
| animate | : True/False 是否为对象设置动画。 | 
| move | : True/False 是否使用键盘移动对象。 | 
| Scaled | : True/False 是否缩放对象图像。 | 
| 绘制。 | : 对象绘制时要调用的函数。 | 
| 状态 | : 对象动画时要调用的函数。 | 
| keypress | : 按键时要调用的函数。 | 
| 鼠标 | : 鼠标事件发生时要调用的函数。 | 

用户可以再次玩游戏。
在接下来的代码中,我们有一个 while 循环,条件为真(将永远工作,直到我们退出)。
要退出循环,我们检查 Game 对象 (oGame) 中的 shutdown 属性。
当用户再次玩游戏时,我们调用 refresh() 方法。
在每场游戏之后,我们使用 callgc() 函数强制调用垃圾回收器。
在游戏结束时,我们使用 delete() 方法删除声音对象。
func playstart oGame
    oSound = New Sound {
        file = "sound/music2.wav"
    }
    while true
        play(oGame)
        if ogame.shutdown = true and oGameState.value = 0
            exit
        ok
        ogame.refresh()
        callgc()
    end
    oSound.Delete()
以下代码用于玩游戏。
我们有 30 个关卡要通过,每个关卡提供与关卡号相同的敌人数量。
您可以使用箭头键(上、下、左、右)移动,并用空格键射击敌人。
在移动设备上玩游戏时,触摸任何位置,游戏将检查方向并向该方向移动(一次一步)。要射击,请触摸玩家本身。您可以在桌面上测试此功能并使用鼠标玩。
您在整个游戏中拥有 100 点能量。每个击中您玩家的敌人将使您的能量减少 10 点。
每个精灵都有 state 属性,我们将一个匿名函数分配给这个属性,该函数将从游戏引擎循环中调用,在每次帧更新之前。
state 函数将接收两个参数:游戏引擎对象和精灵对象。使用这两个参数,我们可以更新游戏状态。
使用游戏引擎对象,可以在运行时向游戏中添加和删除新对象(开火)。
func play oGame
    oGame
    {
        FPS = 60
        FixedFPS = 120
        title = "Stars Fighter!"
        sprite
        {
            file = "images/stars.jpg"
            x = 0
            y = 0
            point = -370
            direction = ge_direction_dec
            type = ge_type_background
            state = func ogame,oself {
                    oself {
                        if x < -350
                            direction = ge_direction_inc
                            point = 370
                        but x = 0 and direction = ge_direction_inc
                            direction = ge_direction_dec
                            point = -370
                        ok
                    }
                }
        }
        sprite
        {
            file = "images/player.png"
            transparent = true
            type = ge_type_player
            x = 400 y =400 width=100 height=100
            animate=false move=true Scaled=true
            mouse = func ogame,oself,nType,aMouseList {
                if not ( aMouseList[GE_MOUSE_X] >= oSelf.x and aMouseList[GE_MOUSE_X] <= oSelf.x+oSelf.width and
                    aMouseList[GE_MOUSE_Y] >= oself.y and aMouseList[GE_MOUSE_Y] <= oSelf.y+oSelf.height )
                    if nType = GE_MOUSE_DOWN
                        if aMouseList[1] < oSelf.X  # left
                            oSelf.X -= 100
                        else
                            oSelf.X += 100
                        ok
                        if aMouseList[2] < oSelf.Y  # up
                            oSelf.Y -= 100
                        else
                            oSelf.Y += 100
                        ok
                    ok
                else
                    if nType = GE_MOUSE_UP
                        cFunc = oself.keypress
                        call cFunc(oGame,oSelf,Key_Space)
                    ok
                ok
            }
            keypress = func oGame,oself,nkey {
                if nkey = key_space
                    ogame {
                        sprite {
                            type = ge_type_fire
                            file  = "images/rocket.png"
                            transparent = true
                              x = oself.x + 30
                            y = oself.y - 30
                            width = 30
                            height = 30
                            point = -30
                            nstep = 20
                            direction = ge_direction_decvertical
                            state = func oGame,oSelf {
                                for x in oGame.aObjects
                                    if x.type = ge_type_enemy
                                        if oself.x >= x.x and oself.y >= x.y and
                                            oself.x <= x.x + x.width and
                                            oself.y <= x.y + x.height
                                            showfire(oGame,x.x+40,x.y+40)
                                            ogame.remove(x.nindex)
                                            oGameState.score+=10
                                            oGameState.enemies--
                                            checkwin(oGame)
                                            exit
                                        ok
                                    ok
                                next
                            }
                        }
                    }
                but nkey = key_esc or nKey = GE_AC_BACK ogame.shutdown()
                ok
            }
            state = func oGame,oSelf {
                oself {
                    if x < 0 x = 0 ok
                    if y < 0 y = 0 ok
                    if x > ogame.screen_w-width  x= ogame.screen_w - width ok
                    if y > ogame.screen_h-height y=ogame.screen_h-height ok
                }
            }
        }
        for g = 1 to oGameState.enemies
            sprite
            {
                type = ge_type_enemy
                file = "images/enemy.png"
                transparent = true
                x = g*random(50) y =g width=100 height=100
                animate=true Scaled=true
                direction = ge_direction_random
                state = func oGame,oSelf {
                    oself {
                        if x < 0 x = 0 ok
                        if y < 0 y = 0 ok
                        if x > ogame.screen_w-width  x= ogame.screen_w - width ok
                        if y > ogame.screen_h-height y=ogame.screen_h-height ok
                    }
                    if random(100) = 1
                        ogame {
                            sprite {
                                type = ge_type_fire
                                file  = "images/rocket2.png"
                                transparent = true
                                x = oself.x + 30
                                y = oself.y + oself.height+ 30
                                width = 30
                                height = 30
                                point = ogame.screen_h+30
                                nstep = 10
                                direction = ge_direction_incvertical
                                state = func oGame,oSelf {
                                    x =  oGame.aObjects[oGameState.playerindex]
                                    if oself.x >= x.x and oself.y >= x.y and
                                       oself.x <= x.x + x.width and
                                       oself.y <= x.y + x.height
                                       if oGameState.value > 0
                                           oGameState.value-=10
                                       ok
                                       ogame.remove(oself.nindex)
                                       checkgameover(oGame)
                                    ok
                                }
                            }
                        }
                    ok
                }
            }
        next
        text {
            size = 30
            file = "fonts/pirulen.ttf"
            text = "Destroy All Enemies!"
            nstep = 3
            color = GE_COLOR_GREEN
            x = 100    y=50
            direction = ge_direction_incvertical
            point = 500
        }
        text {
            animate = false
            point = 400
            size = 30
            file = "fonts/pirulen.ttf"
            text = "Score : " + oGameState.score
            x = 500    y=10
            state = func oGame,oSelf { oSelf { text = "Score : " + oGameState.score } }
        }
        text {
            animate = false
            point = 400
            size = 30
            file = "fonts/pirulen.ttf"
            text = "Energy : " + oGameState.value
            x = 500    y=50
            state = func oGame,oSelf { oSelf { text = "Energy : " + oGameState.value } }
        }
        text {
            animate = false
            point = 400
            size = 30
            file = "fonts/pirulen.ttf"
            text = "Level : " + oGameState.level
            x = 500    y=90
        }
    }
以下函数用于游戏结束(检查我们是否有获胜者或游戏结束)。
func checkwin ogame
    if oGameState.gameresult  return ok
    if oGameState.enemies = 0
        oGameState.gameresult = true
        oGame {
            if oGameState.level < 30
            text {
                point = 400
                size = 30
                file = "fonts/pirulen.ttf"
                text = "Level Completed!"
                nStep = 3
                x = 500    y=10
                state = func ogame,oself {
                    if oself.y >= 400
                        ogame.shutdown = true
                        oGameState.level++
                        oGameState.enemies = oGameState.level
                        oGameState.gameresult = false
                    ok
                }
            }
            else
            text {
                point = 400
                size = 30
                nStep = 3
                file = "fonts/pirulen.ttf"
                text = "You Win !!!"
                x = 500    y=10
                state = func ogame,oself {
                    if oself.y >= 400
                        ogame.shutdown = true
                        oGameState.value = 0
                    ok
                }
            }
            ok
        }
    ok
func checkgameover ogame
    if oGameState.gameresult  return ok
    if oGameState.value <= 0
        oGameState.gameresult = true
        oGame {
            text {
                point = 400
                size = 30
                nStep = 3
                file = "fonts/pirulen.ttf"
                text = "Game Over !!!"
                x = 500    y=10
                state = func ogame,oself {
                    if oself.y >= 400
                        ogame.shutdown = true
                    ok
                }
            }
        }
        showfire(oGame,oGame.aObjects[oGameState.PlayerIndex].x+40,oGame.aObjects[oGameState.PlayerIndex].y+40)
        oGame.aObjects[oGameState.PlayerIndex].enabled = false
        oGame.remove(oGameState.PlayerIndex)
    ok
以下函数用于开火。
此函数使用 animate 类,该类包含以下属性:
| frames | : Number 确定帧数。 | 
| frame | : Number 确定当前帧。 | 
| framewidth | : Number 确定帧的宽度。 | 
| animate | : True/False 决定是否使用动画。 | 
| scaled | : True/False 决定是否缩放图像。 | 
该函数使用 remove() 方法在显示开火图像后从游戏中移除对象。
func showfire oGame,nX,nY
    oGame {
        animate {
            file = "images/fire.png"
            x = nX
            y = nY
            framewidth = 40
            height = 42
            nStep = 3
            transparent = true
            state = func oGame,oSelf {
                oSelf {
                    nStep--
                    if nStep = 0
                        nStep = 3
                        if frame < 13
                            frame++
                        else
                            frame=1
                            oGame.remove(oself.nIndex)
                        ok
                    ok
                }
            }
        }
    }
以下是游戏中关卡(1)的屏幕截图。

以下是游戏状态的类。
我们有 score 属性(在玩家杀死敌人后增加)。
我们有 level 属性(从 1 到 30)。
我们有 enemies count(开始时等于关卡数,然后减少直到玩家通过关卡)。
我们有 playerindex,玩家对象在游戏引擎对象数组/列表中的位置。
我们有 GameResult 属性,决定我们是否有结果(赢/游戏结束)还是没有(仍在玩)。
我们有 StartPlay 属性,决定游戏是否已开始。
class gamestate
    score = 0
    level = 1
    enemies = 1
    value = 100
    playerindex = 2
    gameresult = false
    startplay=false
关注点
游戏在很短的时间内开发完成,我在同一天为我的朋友设计、开发并发布了游戏。后来,我进行了一些微小的改进(没什么难的,这只是一个简单的示例)。
要为游戏构建 Android 包(*.apk),请查看此教程。
历史
本文写于 2016.10.12,旨在提供一个简单的示例,说明如何使用 Ring 编程语言通过 Ring 提供的游戏引擎进行简单的 2D 游戏开发,作为演示项目。


