GMS 2.3 beta : Create a Starfield using Struct

M

MarkWolF

Guest

GM version : 2.3 beta
Download : https://www.dropbox.com/s/2hw0nlnym9h5zif/StarfieldWithStruct.zip?dl=0

Summary : a short and simple tutorial on how to make a starfield using struct, constructor and static functions.
We will render thousands of moving stars with different forms and colors. The size of each star will depend on its current position on the x axis.

Obviously, this is not the most efficient way to render a starfield (e.g. shader, particule system), but I find this project remains a great way to illustrate the potential of GML structs, thanks to its visual aspect.



The ingredients of the recipe:

- 2 or more sprites of stars, glow, ligth (anything you want). I took mine from the GM particules sprites. They are 64 x 64 px, and white (perfect for color blending);
- 1 room : not too big (I chose w:683 px , h:384 px), with no viewport enabled;
- 1object: name it 'oStarfield' for instance.

Within this object you will only have a create and a draw event.

oStarfield: create event:

GML:
/// @description star class and array
// create a star constructor function and an array of star 'objects'


// global var : speed used in the static renderTheStars function
global.starSpeed = 15; 


//---------------------------- Star class ------------------------------------------------------------------------------------//

Star = function()constructor {
    
    xx = irandom_range(-room_width, room_width); // x position picked between -rmw and rmw
    yy = irandom_range(-room_height, room_height); // if your room dimensions differ from your view port dimensions, use wport and hport
    z = irandom(room_width); // 3rd dimension, the depth. The one that will make move your stars
    spr = choose(sLight, sSparkle); //  I chose 2 grey scale sprites but you can have more
    color = make_color_rgb(17*random(4), 50 * random(2), 105*random(2));// adding some color to our sprites
    
    static renderTheStars = function(){      // we want the function static to avoid creating it each time we create a star instance
        
        // update the z value
        z-= global.starSpeed;
        if(z<1) resetTheStars(); // if you don't do that your stars will then come back to the center of the screen
        
        // figure star x and y positions according the xx, yy, and z values
        var sx = ((xx/z) * room_width) + (room_width/2); // divide xx by z and get a value between 0 and  1. Multiply by rmw to scale it properly
        // then translate towards the center of the room/screen
        var sy = ((yy/z) * room_width) + (room_height/2);
        
        // scale the sprite according to its distance from the 'camera'
        // simple but inaccurate way : map the radius(scale factor) according to xx / z
        var r = xx/z;
        
        draw_sprite_ext(spr, 0, sx, sy, r, r, 0, color,1 /*.5 + abs(r)*/); // you can use abs(r) to modify the opacity of the sprite according from the camera
        
        
    }
    
    // create another static function to reset the star position when z<1
    static resetTheStars= function(){
        xx = irandom_range(-room_width, room_width);
        yy = irandom_range(-room_height, room_height);
        z = irandom(room_width);
        
    }
    
    
}


// we need to populate an array of star 'objects'

arrayOfStars = [];
numberOfStars = 3000; // loads of stars

var i = 0;

repeat(numberOfStars){
arrayOfStars[@i]= new Star(); // syntax : starts with 'new' keyword, ends with brackets. No argument to pass in the brackets as this  constructor function does not take anyone  
i++;   
}

The idea is to instantiate how many stars we want (we can) and push them in an array. To do so, we use the constructor function to create a Star class. This class contains everything we need to set the properties, update and render our stars (further details are included in the comments of the code).

Within the constructor function we set a bunch of variables (x,y,z,sprite, color) which will be proper to each star created.

Then we define two functions :

The first one (renderTheStars) enables to update the position of a star and draw it to the screen. Note that we use the 'draw_sprite_ext()' function within a create event o_O
The second one (resetTheStars) resets the position of a star when its z value is below one. This will prevent the star from turning around and heading towards the center of the screen.
Note that these two functions have been defined using the keyword 'static'. So they will only be created once when creating the Star class. They will nevertheless be accessible to each star instance (of the Star class).

Then, below the constructor function, we initialize an array, and assign to a variable the number of stars we want to create. Finally, we use a loop to fill the array with star instances.


oStarfield: draw event:

GML:
/// @description render starfield
// render stars

var i = 0;

repeat(numberOfStars){
    
    arrayOfStars[@i].renderTheStars();
    i++;
    
}

We loop through the array of stars. Each star calls the rendering function attached to the Star class.
We only need 3 or 4 lines of code to draw thousands of stars.


Run and enjoy:

That's it, we're done! All you have to do is put oStarfield in the room and click on 'Run'.


Really finished? Maybe not? Why not play on the opacity of the sprites in the update function or modify them in the reset function? Why not take into account, in the update function, the position of the mouse to determine the vanishing point of the perspective? Or create a subclass of the Star class that would create asteroids? Or what do I know?
 
Top