65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.71/5 (7投票s)

2016 年 10 月 12 日

CPOL

6分钟阅读

viewsIcon

8377

downloadIcon

95

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 游戏开发,作为演示项目。

© . All rights reserved.