Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Developing the Stars Fighter Game for Desktop and Android using the Ring Programming Language

0.00/5 (No votes)
11 Oct 2016 1  
Stars Fighter is a simple 2D Game developed using the Ring Programming Language for Desktop and Android.

Introduction

The Ring Programming Language comes with a simple 2D Game engine (written in Ring) for prototyping simple 2D Games for Desktop and Mobile platforms, In this article we will create the Starts Fighter Game as a simple example about using the Game Engine to create games in little hours.

Background

To understand this article you need to know about programming basics in general and the simple concepts behind simple 2D Games development like (Images, Sprites and Collision detection). Also basic understanding of the Ring programming language is required (or at least experience from using a similar dynamic scripting language like Python and Ruby).

The game engine uses the Allegro game programming library for desktop development and the LibSDL library for Mobile Development. You will write the code once without direct interaction with these libraries where the engine provide a simple layer to concentrate on the game itself.

Check the next articles about Ring

(1) The Ring Programming Language

(2) Syntax Flexibility in the Ring Language

And the next Tutorials

(1) Getting Started

(2) Control Structures

(3) Functions

(4) Lists

(5) Classes

(6) Functional Programming

(7) Declarative Programming 

Using the code

The game start by providing a screen that display the game title and version.

We will have music in the background and once the user press the space key, click using mouse or touch the mobile screen the game will start.

At first we will use a global variable (oGameState), We will store the game state in this object.

To start using the game engine we will using : Load "GameEngine.ring"

Then we will define the main function, create the game object and start the while loop

We will use the next classes

sprite : Create new Sprite object and add it to the game objects.
text : Create new Text object and add it to the game objects.
sound   : Create new Sound object and add it to the game objects
   
C++
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

The text class requires the text positon (x,y) and the font file name (*.ttf) and the font size

The sound class requires the sound file name (*.wav)

The Sprite class provides more attributes. We have  (x,y,width and height) for position.

We have other attributes like

image :String determine the image file name.
point : Number determine the limit of automatic movement of the object.
direction : Number determine the direction of movement.
nstep : Number determine the increment/decrement during movement.
type : Number determine the object type in the game (Optional).
transparent : True/False value determine if the image is transparent.

 

The sprite class get the next attributes by inheritance too

enabled : True/False determine the state of the object (Active/Not Active)
x : Number determine the x position of the object.
y : Number determine the y position of the object.
width : Number determine the width of the object.
height : Number determine the height of the object.
nIndex : Number determine the index of the object in objects list.
animate : True/False to animate the object or not.
move : True/False to move the object using the keyboard or not.
Scaled : True/False to scale the object image or not.
draw : Function to be called when drawing the object.
state : Function to be called for object animation.
keypress : Function to be called when a key is pressed.
mouse : Function to be called when a mouse event happens.

The user can play again

In the next code we have a while loop, the condition is true (will work foreever until we exit from it)

To exit from the loop we check the shutdown attribute in the Game Object (oGame)

When the user play again, we call the referesh() method 

After each game we force calling the Garbage Collector using the callgc() function

In the end of the game we delete the sound object using the delete() method

C++
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()

The next code for playing the game.

We have 30 levels to pass, each level provide number of enemies as the level number.

You can move using arrows (Up, Down, Left and Right) and you can shoot the enemy using Space

When playing using the mobile touch any location and the game will check the direction and move toward that direction (one step only). to shoot touch the player itself. You can test this on desktop and play using the mouse.

You have energy (100) for the complete game. Each enemy hit your player will decrease your energy by 10.

Each sprite have the state attribute, we assign an anonymous function to this attribute and this function will be called from the game engine loop before each frame update.

The state function will get two parameters, The Game Engine Object and the Sprite Object, Using the two parameters we can update the game state.

Using the Game Engine Object and during the runtime new objects are added and deleted from the game (Fire).

 

C++
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
        }
    }

 

The next functions for the end of the game (Check if we have a winner or a Game Over).

C++
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

The next function for fire

This function uses the animate class which contains the next attributes

frames : Number determine the number of frames
frame : Number determine the active frame
framewidth : Number determine the frame width.
animate : True/False determine using animate or not.
scaled : True/False determine scaling image or not.

 

The function uses the remove() method to remove the object from the game after displaying the fire images.

C++
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
                }
            }
        }
    }

 

The next screen shot for the level (1) in the game.

The next class for the game state

We have the score attribute (increased after the player kill an enemy)

We have the level attribute (from 1 to 30)

We have the enemies count (start as the level count then decreased until the player pass the level)

We have the playerindex, The location of the player object in the game engine objects array/list.

We have the GameResult attribute, determine if we have a result (win/game over) or not (still playing)

We have the StartPlay attribute, Determine if the game started or not.

C++
class gamestate
    score = 0
    level = 1
    enemies = 1
    value = 100
    playerindex = 2
    gameresult = false
    startplay=false

Points of Interest

The game is developed in little hours, I designed, developed and released the game for my friends in the same day, Latter along the time I added very little improvements (No hard work, It's just a simple sample).

To build the Android Package (*.apk) for the game, Check this tutorial 

History

This article is written in 2016.10.12 to provide a simple example about using the Ring programming language for simple 2D Games development using the Game Engine provided by Ring as a demo project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here