Monkey (programming language)

Monkey
Paradigm(s) multi-paradigm: structured, imperative, object-oriented, modular, reflective, generic, concurrent
Appeared in March 1, 2011; 2 years ago (2011-03-01)[citation needed]
Designed by Mark Sibly
Developer Blitz Research Ltd.[1]
Stable release 0.70(f) (May 20, 2013; 3 days ago (2013-05-20))
Typing discipline Static, Weak, Strong (Optional), Safe, Nominative, Partially Inferred
Influenced by BlitzBasic, BlitzMax, C#, Java
Platform Crossplatform
OS Microsoft Windows, Mac OS X, Linux (IDE not available). (See the targets for a full list of platforms)

Monkey is a high-level programming language designed for developing games for many different platforms, including desktop & laptop computers, mobile phones & tablets, and video game consoles. The language itself is an object-oriented dialect of BASIC, which the compiler translates into native source code for several target platforms, which is then compiled normally. Currently the official target platforms include: Windows, Mac OS X, Linux, Windows 8/Windows RT (Windows Store applications), Windows Phone 8, Xbox 360 (Based on C# & XNA), Windows Phone 7 (Also XNA based), Android, iOS, PSS/PSM (Deprecated/Suspended, C# & Mono based), HTML5 (Which is primarily based on JavaScript), and Flash. Monkey was created by Blitz Research Ltd, the makers of: BlitzBasic, Blitz3D, BlitzPlus, and BlitzMax.

Community-driven/user-made targets have also been created, some notable user-targets include: MonkeyMax (BlitzMax),[2]Monkey-Python (Python),[3] and a Nintendo DS target.[4]


History

Monkey was released on March 1st, 2011[citation needed], and it was designed by Mark Sibly of Blitz Research Ltd.[5] Monkey's principal implementation is as a source to source compiler that is able to convert Monkey source code to a number of other high-level languages (listed below).[6] The language is primarily focused on multi-platform game development. It supports object-oriented and generic programming, and has a strong type system similar to Java.

Since version 0.59, Monkey has integrated a built-in debugger for its C++ based targets.[7]

↑Jump back a section

Mojo

Mojo is the official proprietary graphics 'module' for Monkey. Mojo has been written for all of Monkey's official game-based targets, this is done using the native programming languages of each target. Mojo is then wrapped using Monkey's "external-language" system. Mojo was designed primarily for writing simple 2D games in Monkey, but like many other APIs, it can also be used for other types of programs. Mojo also has primitive 3D capabilities,[8] but its recommended to use a native graphics library for that platform (There are also OpenGL wrappers that come with Monkey). Like Monkey itself, one of Mojo's goals is to make everything (Graphics and sound in this case) as consistent as possible on all of its supported platforms. Because of this, Monkey-based games tend to look almost identical on all platforms, despite sometimes being based on drastically different technologies.

Mojo currently doesn't render with WebGL for its HTML5/JavaScript versions, however, a member of Monkey's community known as Devolonter has done it himself.

↑Jump back a section

Official targets (Garbage-collected)

  • Deprecated/suspended targets:
↑Jump back a section

Official targets (Not Garbage-collected)

  • 'Standard' C++[10] (Used for console applications and tools. Monkey's translator was built using this.)
↑Jump back a section

Unofficial/community-driventTargets

You can find most of Monkey's unofficial/community driven targets here: The community's "User Targets" section.

↑Jump back a section

Notable games made using Monkey

↑Jump back a section

Sample code

' This example relies on the 'Mojo' module, so it will not compile with the STDC++ target.
' Mojo comes with all full versions of Monkey, and it works on all official game-targets.
' Classes and functions such as Image, and App, LoadImage, and DrawImage are located in Mojo.
 
' Enable strict mode.
Strict
 
' Import the mojo module.
Import mojo
 
' The entry point for monkey:
Function Main:Int()
  New GameApp
  Return 0
End
 
' The main class which expends Mojo's 'App' class:
Class GameApp Extends App
  Field player:Player
 
  ' Override Mojo's 'OnCreate' method:
  Method OnCreate:Int()
    ' OnCreate is called automatically when the 'App' class is created.
 
    ' Most media should be stored in a folder called "ProjectNameHere.data".
    ' Load the image player.png, then return the 'Image' class to 'img'.
    ' Mojo assumes that what you're loading is in the "ProjectNameHere.data" folder.
    Local img:Image = LoadImage("player.png")
 
    ' Create a new instance of our player class using the image we loaded.
    player = New Player(img, 100, 100) ' Self.player could also be used here.
 
    ' Set the update-rate to 60 frames per second.
    SetUpdateRate(60)
 
    ' Returning zero usually signifies that there were no errors in a method like this.
    Return 0
  End
 
  ' Override Mojo's OnUpdate method:
  Method OnUpdate:Int()
    ' OnUpdate is called automatically several times per second.
    ' The number of times this is called is based on the update-rate.
 
    ' Add '1.0' to the player object's 'x' variable(Float).
    player.x += 1.0
 
    ' If the value of 'x' exceeds 100, set it to zero:
    ' This could also be done using 'Mod', the modulus operator.
    If (player.x > 100) Then
      player.x = 0
    Endif ' End could be used here instead of Endif
 
    ' Everything went according to plan, now return zero.
    Return 0
  End
 
  ' Override Mojo's OnRender method:
  Method OnRender:Int()
    ' OnRender is usually called as many times as OnUpdate.
    ' Normally, all graphical/drawing operations must be done in here.
    ' However, you can manually render by using BeginRender() and EndRender().
 
    ' Clear the screen, then display a color based on the values specified(RGB).
    Cls(32, 64, 128)
 
    ' Call our player object's 'Draw' command.
    player.Draw()
 
    ' Everthing went according to plan, now return zero.
    Return 0
  End
End
 
' The player class that was referenced above:
Class Player
  ' Define all of our fields (Variables):
  Field x:Float, y:Float
  Field image:Image
 
  ' Overloading 'New' works the same way as a constructor in other languages.
  Method New(img:Image, x:Float=100, y:Float=100)
    ' Due to the arguments, 'Self' is required in this situation:
    Self.image = img
    Self.x = x
    Self.y = y
  End
 
  ' 'Draw' is a simple command that draws our image using the coordinated specified.
  Method Draw:Void()
    DrawImage(image, x, y)
  End
End
↑Jump back a section

Interfacing with External languages

Monkey provides a relatively simple way to let you work with the language it's going to convert to, it does this using an external-code directive, which it calls "Extern". Like how Monkey handles Public and Private directives, any code written after an external-code directive is considered external, and will not be treated the same as normal Monkey code. When Monkey converts itself into the desired language, it uses all of the "prototypes" specified after an "Extern" directive as macros for the native code you wrote.

Here's an example of how Monkey's external code would be setup:

#If CONFIG = "debug"
    #DEBUG_ENABLED = True
#Else
    #DEBUG_ENABLED = False
#End
 
' You may want to put this in a sub-folder (Mojo and other modules use "native" for native source code).
Import "InsertNameHere.cpp"
 
' You can optionally add 'Private' to an external directive. Example: "Extern Private"
Extern ' All code after this point is used as prototypes for C++ code.
 
' This bit tells Monkey that the native class called "native_Test", will be called Test when used in Monkey.
' This same thing can be done with other things such as functions, methods, fields, etc.
Class Test="native_Test"
    ' Global variables:
    Global GlobalVariable:Int="globalVariable"
 
    ' Fields:
    Field FieldVariable:String="fieldVariable"
End
 
Public ' Everything after this point is public Monkey code.
 
Function Main:Int()
    Local T:Test = New Test()
 
    Return 0
End

If you're only interested in seeing the final code example itself, please scroll down. Here's some needed information about using C++ and Monkey together (Mainly for GLFW users):

    // If you're using a higher-level language like C# or Java, it tends to be a lot more stream-lined with Monkey.
 
    // C++ Specific Notes (Does not apply to most of the external languages):
    // This native-code was written with the GLFW target in mind,
    // it may or may not work with the iOS and Windows 8/Windows Phone 8 targets,
    // and definitely will not work with the "Standard C++" target.
    // However, that's not to say that the external directive doesn't work on these platforms.
 
    /*
    For the record, when using external C++ code with Monkey, unless you specifically set it up,
    your code is generally not going to be garbage collected.
    So for the most part, you will need to handle some things yourself.
    */
 
    /*
    If you're using external C++ code in your game/other, and you're only using small things like functions,
    these next two sections don't really apply to you. Scroll down for the C++ example.
    */
 
    /* Automatic memory management with Monkey and GLFW(C++):
    For the most part, all you need to do is setup your classes with a bit of code for garbage collection.
    Here's an example of an automatically managed/garbage-collected class:
    */
 
    class myClass : public Object
    {
        public:
            // When you're done with an object made from this class, please make sure to clean up anything in this class that isn't garbage collected.
            // Members are not specifically garbage collected just because objects made using this class are.
            myClass() { } // Insert your code here (Initialize things as you normally would)
 
            myClass* m_new()
            {
                // Look below for an explanation about the preprocessor.
                // These parts with the preprocessor are 100% optional.
                #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
                    DBG_ENTER("Native: myClass")
 
                    // This only works with Visual C++ (On Windows), so you may want to remove it
                    DBG_LOCAL((myClass*)this, "Self")
 
                    DBG_INFO("Unable to find location."); // If you want to add the current file's name, you can.
                #endif
 
                return this;
            }
 
            void mark() { Object::mark(); return; }
 
            #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
                String debug()
                {
                    String t = "(myClass)\n"; // This is what's displayed on the side of the IDE.
 
                    // If you want to add variables so the debugger can see them, use this.
                    // t += dbg_decl("Display name of variable", &ActualVariable);
                    return t;
                }
            #endif
 
            // Add anything else you want to this class. (ints, floats, structs, pointers, etc)
    };
 
    // The following debug-related code should be optional, but still very useful for debugging:
    #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
        String dbg_type(myClass**m) { return "myClass"; }
 
        // If you want to do this, you will need to setup the preprocessors yourself.
        // Simply add something like this to your Monkey code:
        /*
        #If CONFIG = "debug"
            #DEBUG_ENABLED = True
        #Else
            #DEBUG_ENABLED = False
        #End
        */
    #endif
 
    // When creating this class, you'll need to do something like this:
    void FunctionNameHere()
    {
        myClass* m = ((new myClass)->m_new());
 
        // [Insert code using the object as you normally would here] (With pointers)...
 
        // Then when you're done, make sure to remove any references to it, and it should get collected.
        m = NULL; // Or nullptr, either should work.
 
        return;
    }
 
    /* Manual memory management with Monkey and GLFW(C++):
    When using C++ external code (with GLFW), there's a number of ways to free things from ram manually.
    One way is to make a class that contains your class, and go from there.
    Another way is to delete things 100% manually,
    the biggest downside to this is that Monkey still thinks it exists, so you will need to set your variable to null.
 
    A work around for that would be using a Monkey-based class to hold the native (C++) class,
    and simply having both objects keep track of each other, which is similar to the other method, and is what I'm doing here.
 
    If you're interested in this, try adding this after an "Extern" directive: Function Delete:Void(O:Object)="delete"
    Edit that bit of code if you need to, you could always just make overloads, and use your external class itself as the argument.
 
    Two of the bigger issues with manual memory management are that your class needs to extend the public 'Object' class in debug-mode,
    and debugging code like that tends to break very easily.
    */


    // After all of that, the end result is this:
    class native_Test : public Object
    {
        public:
            // Constructor(s):
            native_Test()
            {
                Print("Hello, I'm an external object.");
            }
 
            //~native_Test(); <- The destructor isn't needed for this specific class.            
 
            native_Test* m_new()
            {
                #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
                    DBG_ENTER("Native: Native_Test")
                    //DBG_LOCAL((native_Test*)this, "Self")
                    DBG_INFO("(C++ NATIVE)");
                #endif
 
                return this;
            }
 
            void mark() { Object::mark(); return; }
 
            #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
                String debug()
                {
                    String t = "(Native: Test)\n";
                    t += dbg_decl("FieldVariable", &fieldVariable);
 
                    return t;
                }
            #endif
 
            // Global/Static variables:
            static const int globalVariable = 1024;
 
            // "Fields":
            String fieldVariable;
    };
 
    #if defined(CFG_DEBUG_ENABLED) && CFG_DEBUG_ENABLED == 1
        String dbg_type(native_Test**t) { return "Native: Test"; }
    #endif


Final notes:

  • Less modular languages (C++, JavaScript, etc) tend to just throw all of the code into one file.
  • C++ based targets cannot include header files (From within Monkey), you'd need to change their file extension to "cpp".
  • C++ based targets can in-fact use namespaces when using an alias for native code. Here's an example: Function MyFunction:Void()="myNamespace::myFunction"
↑Jump back a section

See also

  • HaXe - a programming language that compiles to JavaScript, C++, Java, C#, and several other languages
↑Jump back a section

Read in another language

This page is available in 2 languages

Last modified on 23 May 2013, at 06:11