Window boarder

przemek

Member
Joined
Sep 18, 2017
Hi, I am trying to find a module that will allow me to create impassable barrier around my screen, so my little playable figure cannot escape. Can you direct me in the way for the function.
 

Gerry Quinn

Active Member
Joined
Jun 24, 2017
That's a pretty standard piece of code - I doubt you will find a module for it!

Something like:

Code:
    xPlayer = Clamp( xPlayer, xMin, xMax )
    yPlayer = Clamp( yPlayer, yMin, yMax )
...should do it. Call it after you have moved the player's position each update, and he will be sent back to his box if he wandered outside it.

You can take xMin as half the player character's size greater than the actual left edge, so he won't overlap it, and so on.
 

przemek

Member
Joined
Sep 18, 2017
I have inserted the code into my code and it gives me syntax error - expecting "(". Here's my code, maybe I just inserted it into wrong on method (tried create and update)
Code:
Import mojo

Class Game Extends App

    Field x:Float
    Field y:Float
     Field character:Image
     Field p1:Game
    
     Method OnCreate:Int()
     character = LoadImage("character.png")

     SetUpdateRate 60
    
     Return 0
    
     End method
    
     Method OnUpdate:Int()
     If KeyDown (KEY_LEFT) Then x = x - 4
    If KeyDown (KEY_RIGHT) Then x = x + 4
    If KeyDown (KEY_UP) Then y = y-4
    If KeyDown (KEY_DOWN) Then y = y +4
   
    xPlayer = Clamp (x:character, x:0, x:900)
     yPlayer = Clamp (y:character, y:0, y:500)
     SetUpdateRate = 60
    Return 0
   
    End Method
   
    Method OnRender:Int()
    DrawImage character, x, y
   
    Return 0
   
    End Method
   
End Class

Function Main:Int()
    New Game()
   
       Return 0
End Function
 

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
You have made the mistake of copying the snippet that Gerry posted without understanding it. The documentation is somewhat lacking in detail for many functions and methods. MonkeyX and Cerberus are not really geared for those just starting to learn programming.

Function Clamp : Int ( x:Int, min:Int, max:Int )
Function Clamp : Float ( x:Float, min:Float, max:Float )

Clamps x to the range min through max inclusive.

You pass the value you wish to clamp (in this case x as in the function prototypes above) and force that value to conform to the range of min and max passed. The function will always return a value back in the range of min and max.

So if you are trying to clamp the x position on screen of a game object, you pass the variable that holds that position and the returned value needs to passed back into that variable.
Code:
x=Clamp(x,0,900)
y=Clamp(y,0,500)
Note: There is no automatic conversion of non literal data types (i.e. those passed as variables). This means that you cannot pass one parameter as a float and the rest as integers, or visa-versa. You therefore have to cast the offending data type to the correct data type of the others. e.g
myFloatX = Clamp(myFloatX, Float(myMinInt), Float(myMaxInt))

xPlayer = Clamp (x:character, x:0, x:900)
What you are doing here will cause a syntax error. You cannot pass colons as part of a call to function/method parameter. Colons are used when declaring variable types.
 
Last edited:

Gerry Quinn

Active Member
Joined
Jun 24, 2017
Thanks Dawlane, you saved me some explanations. xPlayer and yPlayer are just intended as placeholders for whatever the OP calls the x and y position of the player on the screen.

And yes, Monkey/Cerberus is a small community. If you are just starting to program, the way Monkey code works is just as sensible or silly as Java (for example) - but it will be easier and quicker to find answers to common Java questions on the interwebs. Java and Python are probably good beginners' choices nowadays - or C/C++ if they are the sort of beginners who like to start with the fundamentals.

That said, maybe przemek figured it out and got it running now and wants to do more ... in which case, welcome przemek!
 

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
Well Gerry if he hasn't, here's a working example for him to examine.
Code:
' You should always use Strict mode as it help to trap programming errors
Strict
Import mojo

Global arena:CArena ' Variable representing the playing area

' The use of constants is recommended if you have lots of literal numbers that are related and you want to make a quick change.
' You only have to change the constant and not evey occurance of that literal number.
Const pixelsX:Int=4
Const pixelsY:Int=4

' A Class that represents the playing Arena
Class CArena
    Field x:Int
    Field y:Int
    Field w:Int
    Field h:Int
    Field lineWidth:Int
 
    ' Set some defaults that can be over ridden
    Method New(x:Int = 0, y:Int = 0, w:Int = 1, h:Int = 1, lineWidth:Int = 1)
        ' The use of Self helps to distinguish between the class variable and a local variable of the same name
        ' Self is a special variable that basically means this class.
        Self.x = x
        Self.y = y
        Self.w = w
        Self.h = h
        Self.lineWidth = lineWidth
    End Method
 
    ' Note that the variables x amd y for the sprite object are float data types
    ' and the arena field are ints. Therefore the arena variable must be cast to floats
' as we don't what to mess up the fraction parts of the values passed and returned.
    ' There is no automatic conversion and it would generate an unable to determine type error.
    Method BoundsX:Float(x:Float, w:Int)
        Return Clamp(x, Float(arena.x + Self.lineWidth), Float((arena.w - Self.lineWidth) - w))
    End Method
 
    Method BoundsY:Float(y:Float, h:Int)
        Return Clamp(y, Float(arena.y + Self.lineWidth), Float((arena.h - Self.lineWidth) - h))
    End Method
 
    ' Change the arena wall thickness
    Method IncLineWidth:Void()
        lineWidth += 1
    End Method
 
    Method DecLineWidth:Void()
        lineWidth -= 1
    End Method
 
    ' Draw the arena walls
    Method DrawBounds:Void()
        SetColor(255,0,0)
        DrawRect(0,0,w,lineWidth)    ' Top
        DrawRect(0,h-lineWidth,w,lineWidth)    ' Bottom
        DrawRect(0,0,lineWidth,h)    ' Left
        DrawRect(w-lineWidth,0,lineWidth,h)    ' Right
        SetColor(255,255,255)
    End Method
 
End Class

' Very rough sprite object encapsulation
Class CSpriteObject
    Field x:Float
    Field y:Float
    Field w:Int
    Field h:Int
    Field img:Image
 
    ' Load image and create sprite object
    Method New(imgPath:String, x:Int = 0, y:Int = 0)
        Self.x = x
        Self.y = y
        img = LoadImage(imgPath)
        Self.w = img.Width() ' Store the width and height of the image to avoid having to call the image method to retrieve them all the time.
        Self.h = img.Height()
    End Method
 
    ' Move the sprite in a direction and check the arena bounds
    Method Left:Int(x:Float = 1)
        Self.x = Self.x - x
        Self.x = arena.BoundsX(Self.x, Self.w)
        Return Self.x
    End Method
 
    Method Right:Int(x:Float = 1)
        Self.x = Self.x + x
        Self.x = arena.BoundsX(Self.x, Self.w)
        Return Self.x
    End Method
 
    Method Down:Int(y:Float = 1)
        Self.y = Self.y + y
        Self.y = arena.BoundsY(Self.y, Self.h)
        Return Self.y
    End Method
 
    Method Up:Int(y:Float = 1)
        Self.y = Self.y - y
        Self.y = arena.BoundsY(Self.y, Self.h)
        Return Self.y
    End Method
 
    Method Draw:Void()
        DrawImage(Self.img, Self.x, Self.y)
    End Method
 
End Class

' Main game class
Class Game Extends App
    Field character:CSpriteObject
 
    Method OnCreate:Int()
        SetUpdateRate 60
 
        ' Set the arena size to the device width and height
        arena = New CArena(0, 0, DeviceWidth(), DeviceHeight())
 
        ' Load the image to use as a sprite and set it to the centre of the device. Use your own image
        character = New CSpriteObject("blob.png", DeviceWidth()/2, DeviceHeight()/2)
        Return 0
    End Method
 
    Method OnUpdate:Int()
        ' The sprite object should call any boundary checks after any change. The same goes for any change of the arena
        If KeyDown(KEY_LEFT) Then character.Left(pixelsX)
        If KeyDown(KEY_RIGHT) Then character.Right(pixelsX)
        If KeyDown(KEY_UP) Then character.Up(pixelsY)
        If KeyDown(KEY_DOWN) Then character.Down(pixelsY)
 
        If KeyDown(KEY_Q) And arena.lineWidth < 30
            arena.IncLineWidth()
            character.x = arena.BoundsX(character.x, character.w)
            character.y = arena.BoundsY(character.y, character.h)
        Endif
 
        If KeyDown(KEY_W) And arena.lineWidth > 1
            arena.DecLineWidth()
            character.x = arena.BoundsX(character.x, character.w)
            character.y = arena.BoundsY(character.y, character.h)
        Endif
 
         Return 0
     End Method
 
     Method OnRender:Int()
         Cls
         DrawText("Use arrow keys to move sprite", 40, 40)
         DrawText("Use Q & W to change the arena wall thickness.", 40, 52)
 
         arena.DrawBounds()
         character.Draw()
         Return 0
     End Method
 
End Class

' Program entry point
Function Main:Int()
    New Game()
    Return 0
End Function
 
Last edited:

Gerry Quinn

Active Member
Joined
Jun 24, 2017
No offence, Dawlane, but I think you are on a kick of 'too many objects' here! If you use objects, you will have a Rectangle object that you use all the time [*], and that will do. Anyway, your first post shows what the OP needs to do.

[*] I use Point, Size and Rect classes modelled on the old helper classes that MSVC used to have way back when - I also have floating point variants of each. I will post them if anyone is interested.
 

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
No offence, Dawlane
Non taken.

I would normally have base helper Point, Size and Rectangle classes; extend them when required, or make them object fields within a class and pass object references within method functions instead of passing long lists of parameter. In this case I thought it would be overkill as there are only three objects: the playing arena that is a rectangle with a flexible wall, the object that would represent a player (I just happened to call it a sprite object) and the main program class. Plus I wanted to try and show the issue with Clamp and the data type casting as well as storing and using values returned from functions that never really change to avoid function calling overhead.

I've seen a few examples and tutorials posted around where everything gets thrown into the main game class, then someone new to MX/CX tries to modify the code without any understanding of it and ends up posting "Why doesn't this code work".
 
Last edited:

Phil7

Moderator
Joined
Jun 26, 2017
I wrote just a minimalist example of a working code as I think @przemek is just starting and I can remember me being confused, when to much information came at once.

Code:
Strict

Import mojo

Function Main:Int()
    New Game()
    Return 0
End

Class Game Extends App

    Field playerX:Float = 250
    Field playerY:Float = 250
    Field playerSize:Float = 10
   
    Field BorderXMin:Float = 100
    Field BorderXMax:Float = 400
   
   
    Method OnCreate:Int()
   
        SetUpdateRate(60)
       
        Return 0
    End

    Method OnUpdate:Int()
   
        If KeyDown(KEY_RIGHT)
            playerX = playerX + 2
        EndIf
       
        If KeyDown(KEY_LEFT)
            playerX = playerX - 2
        EndIf
   
       
        playerX = Clamp(playerX, BorderXMin, BorderXMax - playerSize)
       
   
        Return 0
    End
   
    Method OnRender:Int()
       
        Cls(0, 0, 0)
       
        DrawText("Use left and right keys to move the square within the borders.", 10, 10)
       
       
        'Draw lines to make the border visible
        DrawLine(BorderXMin, 0, BorderXMin, 600)
        DrawLine(BorderXMax, 0, BorderXMax, 600)
       
        'Draw player as a square.
        DrawRect(playerX, playerY, playerSize, playerSize)
       
        Return 0
    End
   
End Class
 
Top Bottom