Learn.BlessHayGaming is a blog for game development-related tutorials, tips, tricks and tales. Most of these will be centered around the software GameMaker:Studio. Other tutorials may be centered around Spine, Photoshop, or other software used by Bless Hay Gaming during game development.
Don't wanna be here? Send us removal request.
Text
Easily Flip & Rotate in the Room Editor
View this post on Instagram
In the #GameMakerStudio2 room editor, you can rotate and flip tiles, sprites and objects by tapping Z and X respectively (rotation is tiles only). #GameMaker #GameDev #minitutorial #tutorial
A post shared by Simon Milfred (@blesshaygaming) on Dec 5, 2018 at 5:47am PST
0 notes
Text
Multiple Keys Mapped for the Same Action
Should work in GameMaker:Studio 2 (all versions).
This tutorial is written for GameMaker:Studio 2.
Problem
Oftentimes we want to be able to check multiple optional keys to perform an action. This might be to control a character both with WASD and with the arrow keys, or maybe to allow pausing on both the P key and the Pause key. I see a lot of people handling it like this:
if (keyboard_check(vk_left) || keyboard_check(ord("A"))) { // Do something }
This is of course fine in many cases, but it can turn into some very long conditionals sometimes. Also, if we want to change the controls at some point (or want to allow the player to do so), it becomes quite an inconvenience.
Solution
In GameMaker:Studio 2 you can define arrays like this:
var _array = ["index0", "index2", "etc."];
It is an easy way to pack multiple values into a variable. This does of course not help us directly, as keyboard_check(); does not accept arrays as their argument, but what if we make our own script? A keyboard_check_ext(); script? This would allow us to do something like this instead:
var _left = [vk_left, ord("A")]; if (keyboard_check_ext(_left)) { // Do something } // OR if (keyboard_check_ext([vk_left, ord("A")])) { // Do something }
Would that not be nice? We can even use a global or instance level variable to hold the mapped keys, changing them whenever we want to. But how would such script look?
First and foremost, we want to be able to use the script with singular input as well, so we start off the script by checking if our argument is an array, and if it is not, then we simple do a regular keyboard_check();.
var _array = argument0; if (!is_array(_array)) { return keyboard_check(_array); }
This also allows us to do a Ctrl + Shift + F in our project, and replace all uses of keyboard_check(); with our new script, without anything breaking.
Next we get the length of the array and loop though it. For each element in the array we do a keyboard_check();, and if it returns true, then the loop ends there due to our return. After the loop we make sure to return 0, to let the user know that none of the keys are being pressed. A full script would look something like this:
/// keyboard_check_ext(array_with_keys); /// @desc Is one of multiple keys held down? /// @arg array_with_keys var _array = argument0; if (!is_array(_array)) { return keyboard_check(_array); } var _length = array_length_1d(_array); for(var i=0; i<_length; i++){ if (keyboard_check(_array[i])) { return 1; } } return 0;
Of course the exact same thing can be used for keyboard_check_pressed();, keyboard_check_released();, keyboard_check_direct();, mouse_check_button_pressed();, etc., simply by replacing the function used in the script.
I hope this script and tutorial helped you making even more awesome games, and please share it, if you know someone who could learn something from it.
Kind regards, Simon
0 notes
Text
Easy tile creation using Aseprite! #Aseprite #AsepriteMini
A post shared by Simon Milfred (@blesshaygaming) on Apr 23, 2018 at 11:34pm PDT
0 notes
Video
It's pretty easy to make pickups look more inviting using sinus :D #GameMaker #GameMakerMini
0 notes
Text
Using Aseprite with Spine: Aseprite
Should work for most versions of Aseprite v1.2 beta1 (or newer). Should work for all versions of Spine.
For this tutorial I used Aseprite v1.2.6 and Spine v3.5.46. I personally use Spine, but this method should work for other skeletal animation software as well.
I work on a Windows PC, so I do not know how things may differ on a Mac or Linux PC.
Introduction
During our development of Tale of Omni we use especially two pieces of graphics software: Aseprite and Spine. Aseprite is where we, the Artist and I, draw the graphics. And then, unless the character needs only few or no animations, I animate them in Spine - else they are animated in Aseprite by either of us. When I animate in Spine the character needs to be cut-out into parts, as Spine is software for skeletal animation. To begin with I used Photoshop as an intermediate step for doing this, as you may have seen me explain before.
Today's post will be a tutorial on how to cut out the middle man and make ready-for-Spine content directly in Aseprite.
Starting Your Work in Aseprite
Since this tutorial is very focused, I will work from the assumption that you know your way around at least the basics of Aseprite and Spine.
1) Start off by making a new document big enough to fit everything (you can always resize the canvas as you go, of course). Then make a secondary frame before you draw your parts. Draw on the second frame and let the first frame stay empty - we will be using the first frame for masking later.
If you already have a character ready to cut out, simply insert an empty frame before it.
2) Make your character. Draw each body part on its own layer (or cut out an already existing character) and give the layers suitable names. You do not need to follow my naming conventions, but make sure your names are not too abstract, as these will be part of the exported filenames as well. Also add a tag to the skin describing the "type" of character (this might be easier to do after adding more skins in the following step).
3) Make more characters. Or "skins" for your character. Of course only if more skins are needed. The alternate skins should fit on top of the original one, as they will end up sharing the same skeleton. If they fit the same type as your previous, original skin, group them under the same tag as I have done. Else group them under other tags.
Note, it is possible to make impactful changes if you can visualize how the skeleton will be (or if you return and make skins after the skeleton is developed). For example, my two panda skins have different tail lengths.
4) Time for masking. But before I explain what to do, I will explain why to do it.
When working in Spine, I like to be able to reuse. After setting up my skeleton and my first skin - including making meshes and the like - I would rather not have to set the graphics for the following skins by hand as well (I will go more in depth with this in part two of this subject). But if I simply replace a graphic with another, or if I update a graphic and re-import it, and the graphic is not the exact same size as the previous one, then funky stretching will happen as shown below.
(The image below showcases the small, round panda tail being stretched due to a mismatch with the length of the red panda tale)
This is why I like to make a mask for the graphics. When exporting layers from Aseprite and trimming them (more on this later), the different frames will be trimmed to be of equal size. This means that if a frame is larger in size than another frame (like the red panda tail is longer than the panda tail), the canvas of the panda tail will be enlarged to be the same size.
I use masking to indicate the potential max-size of a given part, thus giving myself room to play around with skins in the future without screwing all my work in Spine up. I do this by going to the first frame, and then I draw pink rectangles on each layer showcasing how big that particular body part should be able to be. Toggling on onion skinning is quite helpful here.
I then put a tag like "Mask" on that frame. It is important to do this after being done with the masking, as onion skinning will only work within that tag afterwards.
5) Now it is time to export the layers. To do this you need to close down Aseprite, as this is done through the Command Line Interface (CLI) (or in our case through a batch file). Aseprite allows for a lot of awesome stuff through CLI - you can read about it here.
You start out by opening notepad. Then you define two variables: One for the path to the Aseprite software, and one for the path to the project file.
I like to make my batch file usable across multiple projects, so instead of writing the name of a project file, I fetch the name of the batch file itself and uses that name by writing "%~n0" (this is what it means). This way I just need to rename the batch file to fit the project I want it to work its magic on. The two variables looks like this in my case because I use Aseprite from Steam:
@set ASEPRITE="C:\Program Files (x86)\Steam\steamapps\common\Aseprite\aseprite.exe" @set FILENAME="%~n0"
I then like to create an asset folder for my parts, which you can do like this (note that I use the previously defined variable for the folder name):
mkdir %FILENAME%-assets
Then comes cutting out the parts. This is done by running %ASEPRITE% with the batch parameter "-b" or "-batch". We tell which file to target, %FILENAM%, and then we apply a series of options:
--trim: We only want the parts to be as big as their masked areas. If our whole project is 96x96 pixels, we do not want each part to be exported with such a big canvas - it is a waste of resources and makes it harder to work with in Spine.
--save-as: We of course want to name our files, and in doing this we can modify what Aseprite exports further: By adding {layer} as part of the filename, we tell Aseprite to export each layer individually, and by setting the file extension to .png, Aseprite knows that we want individual frames. Since we want to differentiate between tags (as we use them as skin type groupings), {tag} makes sense to insert as well, and we can tell which frame in the tag we are looking at by inserting {tagframe}. I personally want it to count like 01-02-03 instead of 0-1-2, so I alter the tagframe like this {tagframe01} (more formatting options can be found here).
Finally our line of code should (or could) look as follows:
%ASEPRITE% -b %FILENAME%.ase --trim --save-as %FILENAME%-assets/{tag}{tagframe01}_{layer}.png
But you might also want to export the assembled characters as an assembly guide to use in Spine. That could look like this:
%ASEPRITE% -b %FILENAME%.ase --trim --save-as %FILENAME%-assets/AssemblyGuide.png
That concludes our batch file, which should then look like this:
@set ASEPRITE="C:\Program Files (x86)\Steam\steamapps\common\Aseprite\aseprite.exe" @set FILENAME="%~n0" mkdir %FILENAME%-assets %ASEPRITE% -b %FILENAME%.ase --trim --save-as %FILENAME%-assets/{tag}{tagframe01}_{layer}.png %ASEPRITE% -b %FILENAME%.ase --trim --save-as %FILENAME%-assets/AssemblyGuide.png
Now you just have to save it. Make sure to give it the exact same name as your Aseprite project file, and in the same folder. The only exception is the file extension. You should save it as a .bat file.
Then simply double-click the batch file to run it. A Command Prompt window will show momentarily, and then a new folder of assets should be generated.
And that's it! At least the part considering Aseprite. From now on, if you make changes to the character or make new skins, you simply delete the asset folder and double-click the batch file to re-generate. That's how easy it is.
1 note
·
View note
Text
Multiple Parents (ish) in GameMaker: Studio
Should work in GameMaker:Studio (all versions). Should work in GameMaker:Studio 2 (all versions).
This tutorial is written for both GameMaker:Studio and GameMaker:Studio 2.
Problem
A question, which I have been asked multiple times concerning GameMaker, is “how can I set two or more parent objects for my object?” The answer, I typically answer, is “you cannot”, and for the longest of time I thought that to be how it is. But I recently figured out a darn simple way to sorta accomplish multiple parents for an object.
I write sorta, because it only partially works: You can have an object inherit code from multiple objects, but you cannot have the object be referred to as multiple parent objects.
Example
Say we have two parent objects: obj_parent1 and obj_parent2, and an object, obj_child, set to be a child of obj_parent1. Normally obj_child would inherit the code from obj_parent1, and if you referred to obj_parent1 in a with statement, then instances of obj_child would be counted as instances of obj_parent1 as well.
Solution
When an object has a parent, and you wish to add code to an event in the child object, yet still have it inherit from the parent object, usually you would use event_inherited(); before or after the added code in the child object. From the manual:
This function will call the current event of the parent object of the instance. […]
To mimic this function but apply it with a secondary parent, we simply need to find a similar function, which can copy the currently executed event’s code but from another object of our choice. Luckily, event_perform_object(); does exactly that!:
This functions works the same as event_perform() except that this time you can specify events from another object. There are many options here which allow complete simulation of all possible events, […]
NOTE: Actions in the event called with this function are applied to the current instance, and not to instances of the given object.
We hit the jackpot! The function takes three arguments: The object to get the code from, the type of the chosen event (ex. ev_create or ev_step) and the number (sub-type) of the chosen event (ex. ev_step_normal or ev_step_end). Since we just want the currently executed event, we can use the built-in variables event_type and event_number to fill out the second and third argument, and all we are left with doing is select which object we want to ‘inherit’ from.
So: For our previous example, we want to put event_perform_object(obj_parent2, event_type, event_number); in all the events of obj_child, in which we want to inherit from obj_parent2. The events in obj_child could then look something like this:
event_inherited(); //Inheriting from obj_parent1 event_perform_object(obj_parent2, event_type, event_number);
But be mindful! The code will run in the order you type it in, so if obj_parent1 contains code to apply movement, but obj_parent2 contains code for setting the movement, you would probably want to inherit from obj_parent2 first.
Extended Usage: Prototypes and State Machines
Another word, we could use instead of a secondary object, is the term prototype. This term covers objects, which will never actually be created in your game, but which contains code you need in other objects. An example usage of this could be for state machines. Here you could make a prototype object for each state (in this case we will just make a simple, general state machine, differentiating between being grounded, being in water, and being in the air):
obj_player : The object using the state machine. proto_obj_on_ground : The player when on the ground. proto_obj_in_air : The player when in the air. proto_obj_in_water : The player when in water.
The step event for obj_player could then be as follows:
var __obj = noone; if place_meeting(x, y, obj_water) { __obj = proto_obj_in_water; } else if place_meeting(x, y+1, par_solid) { __obj = proto_obj_on_ground; } else { __obj = proto_obj_in_air; } event_perform_object(__obj, event_type, event_number);
Even better: Other objects such as enemies can access the same prototypes, thus you don't have to repeat the same code across multiple objects!
Why Not Just Use Scripts?
Yes, you can do the same thing with scripts, I am not going to lie. Prototype objects are simply another place to put the code, and assuming you need different code for multiple different events, I (sometimes) prefer having it all gathered in an object, rather than using 4-5 different scripts. However, it all come down to preferences.
Anyway, that's all for this time. Hope you enjoyed this tutorial!
Kind regards, Simon
1 note
·
View note
Text
Play Spine Animation Tracks Exactly Once in GameMaker:Studio
Should work in GameMaker:Studio EA 1.99.525 (or newer). Should work in GameMaker:Studio 2 (all versions).
This tutorial is written for GameMaker:Studio but everything is applicable to GameMaker:Studio 2.
I used Spine version 3.4.02 for this tutorial. Always use the recommended version of Spine for your version of GameMaker:Studio.
The Problem
While GameMaker:Studio does have an Animation end event, this event only reacts on the main animation track (track 0), and not on all the other tracks. However, using the at-the-time-of-writing new Animation event event, you can now easily make sure that specific animations on any track only play once.
What to Do in Spine
youtube
As the video explains:
You first add the event to your hierarchy.
You then key the event at the last frame of the animations you want to stop after playing once (or wherever you want the animation to stop).
These animations will typically be secondary animations, and rarely the animations you place on track 0, as you can simply use the Animation end event for these, as explained before.
After this you of course export your character and loads it into GameMaker:Studio as a skeletal sprite.
What to Do in GameMaker:Studio
As there has not yet been written a manual entry on the subject of the Animation event event at the time of writing, you will have to use Nocturne's forum post on the topic for reference. Nocturne flesh out multiple other Spine-related updates in this post.
When the sprite is added and attached to an object, we now add the Animation event event to the object as well. You find the event under the Other category. You then add a code block to the event.
The event is triggered by Spine events and automatically generates a ds_map. This ds_map is accessed through the variable event_data local to the event. The ds_map contains multiple keys, but for our purpose we only need two: The "name" key, to check if the current event is the "stop" event, and the "track" key, to check which animation track the animation is running on.
Let's begin writing the code. First we test for the correct event:
if event_data[? "name"] == "stop" { }
Note that I use the accessor "?" to easily look inside the ds_map. After that we simply turn off the animation track in question by using skeleton_animation_clear();:
if event_data[? "name"] == "stop" { skeleton_animation_clear(event_data[? "track"]; }
..and Voilá!
Now your animation will stop after playing exactly once! Yes, it is really that easy. Hope you enjoyed!
Regards, Simon
1 note
·
View note
Text
Testing for Collisions with Multiple Different Objects in GameMaker:Studio
Should work in GameMaker:Studio (version 1.1.1013 or newer). Should work in GameMaker:Studio 2 (all versions).
This tutorial is written for both GameMaker:Studio and GameMaker:Studio 2.
Problem
Sometimes you want to do a place_meeting(); test for multiple objects. Sometimes a parent object can help you do this, but other times you may want more control over, which objects or instances you are testing against. It is the latter, I wish to offer a solution for.
In this tutorial I will focus on the place_meeting(); function, but the concept is applicable to every single, other collision function out there.
Solution
First part of the solution is how we best compile a list of various objects. Here I suggest an array, since these are easy to overwrite and clean up. "But wait!" I hear you say. "place_meeting(); does not accept an array as an argument!?"
Right you are. This is why, the next step is to create a new script, which we will use instead of the function. Let us name the new script something like place_meeting_ext();, so that the name mirrors the extended functionality, and let us use the first line of the script to define the name and the arguments, and the second line to make some temporary variables:
///place_meeting_ext(x,y,array); var _x = argument0, _y = argument1, _a = argument2;
Next thing we need to do, is to loop through the array and do place_meeting(); for all the array's instances or objects - at least until a collision is found. We do this with a for-loop.
///place_meeting_ext(x,y,array); var _x = argument0, _y = argument1, _a = argument2; var _l = array_length_1d(_a); for(var i=0;i<array_length_1d(_a);i++){ if place_meeting(_x,_y,_a[i]) { return 1; } } return 0;
And here we go, a fully functioning script you can use instead of place_meeting(); to test for multiple different objects and instances.
Note that I store the array length in the variable _l, so that the loop does not have to look it up during every iteration. Also, we do not have to break; the loop ourselves upon collision, as the return; will automatically do this for us, as it functions as an exit; statement.
Something Extra
If you want the script to both take arrays and singular object types or instances, you can easily allow for this by doing an is_array(); test, in which case the script would look like this:
///place_meeting_ext(x,y,object|array); var _x = argument0, _y = argument1, _a = argument2; if is_array(_a) { var _l = array_length_1d(_a); for(var i=0;i<array_length_1d(_a);i++){ if place_meeting(_x,_y,_a[i]) { return 1; } } return 0; } else { return place_meeting(_x,_y,_a); }
You can further extend the script to allow for string input as well, so that you can write out the object name:
///place_meeting_ext(x,y,object|array); var _x = argument0, _y = argument1, _a = argument2; if is_array(_a) { var _l = array_length_1d(_a); for(var i=0;i<array_length_1d(_a);i++){ if place_meeting(_x,_y,_a[i]) { return 1; } } return 0; } else { if is_string(_a) { if asset_get_type(_a) == asset_object { return place_meeting(_x,_y,asset_get_index(_a)); } else { return 0; } } else { return place_meeting(_x,_y,_a); } }
Likewise this check could be added to the for-loop as well. The possibilities are truly endless, though be aware, that every line of code makes the script ever so slightly slower to run, so please do only extend the code to the functionalities you need!
Conclusion and Next Step
In this tutorial I have shown you, how you alter place_meeting(); to accommodate for multiple instances or objects through the use of an array. I have also shown you how to extend on this. In my next tutorial I will reverse the topic, and instead show you how to return an array with all the met instances on a given placement.
Feel free to share this post if you enjoyed it!
Until next time, Simon
1 note
·
View note
Text
Timers and Countdowns
Should work in GameMaker:Studio (all versions). Should work in GameMaker:Studio 2 (all versions).
These scripts are written for both GameMaker:Studio and GameMaker:Studio 2.
Introduction
Something I use often when making games are timers and countdowns. Sometimes it is enough to do a simple my_timer++; in the Step event, but sometimes I need more precision. It is for those times I have made the following scripts for easily making timers and countdowns!
The timers and countdowns are initiated like so;
my_timer = timer_create(); my_countdown = countdown_create(10000);
They both function similarly, and are based around milliseconds. Only difference: Timers count up and countdowns count... down.
To update a timer or coundown, which you normally would do once every step, you simply use timer_update(my_timer); or countdown_update(my_countdown);, and to get the current time of the timer or countdown you use timer_get(my_timer); or countdown_get(my_countdown);. This will return how much time has passed for the timers, or how much time is left for the countdowns, both in milliseconds. Divide this with 1000 to get the value in seconds.
Timer Scripts
///timer_create(); //Usage: my_timer = timer_create(); var _a; _a[0] = 0; return _a;
///timer_update(timer); //Usage: timer_update(my_timer); var _a = argument0; _a[@ 0] += delta_time/1000; return _a;
///timer_get(timer); //Usage: time_passed = timer_get(my_timer); var _a = argument0; return _a[0];
///timer_set(timer,time); //Usage: timer_set(my_timer,0); var _a = argument0; _a[@ 0] = argument1; return _a;
///timer_add(timer,amt); //Usage: timer_add(my_timer,1000); var _a = argument0; _a[@ 0] += argument1; return _a;
///timer_subtract(timer,amt); //Usage: timer_subtract(my_timer,1000); var _a = argument0; _a[@ 0] -= argument1; return _a;
///timer_multiply(timer,scalar); //Usage: timer_multiply(my_timer,2); var _a = argument0; _a[@ 0] *= argument1; return _a;
///timer_divide(timer,val); //Usage: timer_divide(my_timer,2); var _a = argument0; _a[@ 0] /= argument1; return _a;
Countdown Scripts
///countdown_create(ms); //Usage: my_countdown = countdown_create(); var _a; _a[0] = argument0; return _a;
///countdown_update(countdown); //Usage: countdown_update(my_countdown); var _a = argument0; _a[@ 0] -= delta_time/1000; return _a;
///countdown_get(countdown); //Usage: time_left = countdown_get(my_countdown); var _a = argument0; return _a[0];
///countdown_set(countdown,time); //Usage: countdown_set(my_countdown,10000); var _a = argument0; _a[@ 0] = argument1; return _a;
///countdown_add(countdown,amt); //Usage: countdown_add(my_countdown,1000); var _a = argument0; _a[@ 0] += argument1; return _a;
///countdown_subtract(countdown,amt); //Usage: countdown_subtract(my_countdown,1000); var _a = argument0; _a[@ 0] -= argument1; return _a;
///countdown_multiply(countdown,scalar); //Usage: countdown_multiply(my_countdown,2); var _a = argument0; _a[@ 0] *= argument1; return _a;
///countdown_divide(countdown,val); //Usage: countdown_divide(my_countdown,2); var _a = argument0; _a[@ 0] /= argument1; return _a;
Example of use
Say we have a game where the room restarts every 60 seconds. A controller object could keep track of this, and be set up like this:
Create event
restart_cd = countdown_create(60000);
Step event
if !global.pause { countdown_update(restart_cd); if countdown_get(restart_cd) <= 0 { room_restart(); } }
Draw gui event
draw_text(10,10,string(ceil(countdown_get(restart_cd)/1000)));
The countdown will then keep counting down unless paused by the global.pause variable. The countdown will be drawn to the screen in seconds, and when the countdown reaches 0 the room will restart.
Behind the Scripts
As you may notice I use one-value-arrays to store the timers and countdowns. This is simple because they have accessors (the @ I use when setting the array), which means you do not have to use the akward my_timer = timer_update(my_timer);. I did leave in returning the array though, in case that is the style you prefer.
A timer system could easily be made, where you do not need the update scripts. In fact, the first iterations of the scripts did not need them, since it was using current_time for the timing. However, I opted to use delta_time instead, because this allows you to still use pause systems and generally gives more control - current_time would still keep counting no matter the state of the game! The delta_time still works across all room speeds and lag.
Hope you will find these scripts helpful!
Until next time, Simon
1 note
·
View note
Text
Delta Time Adventures!
A.K.A. my time implementing delta timing in Tale of Omni.
The Starting Point
After about half a year of development on Tale of Omni, I thought it was about time to think about adding some delta timing. Best thing would have been to make delta timing part of the project from the very beginning, but at least the game had not yet grown to a size, where the addition would be completely unfathomable.
Delta timing, for those unfamiliar with it, is a term used about the time between program iterations (or “steps”), and GameMaker: Studio has a handy, built-in variable delta_time to get this timing in microseconds. When I say, that I wanted to “add it to my game”, I am referring to the usage of delta timing: Making lag less visible.
Normally, when a game encounters lag, the game will continue as-it-was, and the lag will be very noticeable, and planning your movement can become quite hard. To make up for this, you can use delta timing to calculate how far you should have moved in the given time span. If the current step was twice as slow as it should have been, then you need to move everything twice as far!
The Solution
The first part of the solution, is one I think most people ready for delta timing can think up: Changing the delta_time value to a scalar. My game was up until this point made to run at 60 fps, so all my values was made for this speed, and this was the factor I needed to measure against. Fps stands for “frames per second”, and there goes a million microseconds to a second, so first things first I simply had to figure out, how many microseconds an ordinary step at 60 fps should take:
var __rd = 1000000/60;
Now, dividing the time since the last step (delta_time) with this value will then give us our scalar:
DELTA = delta_time/__rd;
This needs to be recalculated once per step! (I do it in the begin step event)
The simplest part is now done. We can multiply speed, image speed, and a lot of other values with this variable (better make it global), but you will quickly run into trouble when you begin multiplying it with gravity, friction, or other accelatory values. I know I did.
Thing is, these types of values affects OTHER values, and not linearly. Let’s say we jump by setting our vertical speed (vspd) to -10, and that we have a gravity (g) of 1. We add one to our speed, then moves, then add one, then move, then add one, then moves... You get the idea. This might look linear, since we add one each step, but the amount we move will be altered each step exponentially! Take a look at this for proof:
Step 0: We move up 10. Total moved: 10. Applies gravity. Step 1: We move up 9. Total moved: 19. Applies gravity. Step 2: We move up 8. Total moved: 27. Applies gravity. Step 3: We move up 7. Total moved: 34. Applies gravity. Step 4: We move up 6. Total moved: 40. Applies gravity. Step 5: We move up 5. Total moved: 45. Applies gravity. Step 6: We move up 4. Total moved: 49. Applies gravity.
If we just multiply both speed and gravity with the DELTA scalar (and the scalar is 4), then we should have a speed of 40, and a gravity of 4. Subtracting the gravity from the speed then gives us a movement of 36 - but wait, if done step-by-step this should be 34 (step 3 is the fourth step)?
At step 1 the gravity (applied after the previous step) has affected the speed by 1. At step 2 the gravity has affected the speed by 3, not 2. At step 3 the gravity has affected the speed by 6, not 3. At step 4 the gravity has affected the speed by 10, not 4. And so it goes on...
So. When we work with accelerations we need a different multiplier, though this multiplier should still be dependent on the DELTA variable!
After doing some math
(I could not get any solutions from around the web to work, so I had to do it myself), I came up with this:
DELTA_ACC = (.5*DELTA*(DELTA+1));
These two scalars ended up being all I needed for my game’s physics! When I add acceleration, I multiply the acceleration with DELTA_ACC, and for other things I multiply with DELTA - simple as that!
..though.. I then found I needed more. I use the lerp() function a lot, and none of these multipliers worked for that. Luckily, that one didn’t take too long, and I came up with this script:
///lerp_dt(amt); return 1-power(1-argument0,DELTA);
Where I normally would write lerp(x,100,.1), now I would simply write lerp(x,100,lerp_dt(.1)) to add the proper amount of lerping.
The End
So far, I have not needed anything else for implementing delta timing, than these three bits of code, and so far it works beautifully! I have tried to keep this post close to the bone, but if you have any questions, feel free to ask!
Hope you enjoyed the read.
Some quick notions:
Spine animations’ image speed is not depending on the room speed (so they need special care)!
Be careful with just subtracting DELTA from alarms - make a script to do this, which runs the alarm’s code if it goes below 1.
...I might add more as I further develop Tale of Omni.
1 note
·
View note
Video
youtube
Spine Tips for GameMaker: Studio - Reuse Meshes in Multiple (Differently Scaled) Attachments
2 notes
·
View notes
Video
youtube
Spine Tips for GameMaker: Studio - Reuse Meshes in Multiple Attachments
1 note
·
View note
Video
youtube
Spine tips for GameMaker: Studio - Animating Pixel Art
1 note
·
View note
Video
youtube
Animating the "Software Bug" enemy for Omni [GameMaker:Studio + Photoshop + Spine]
#SpineVideo#GameMakerVideo#PhotoshopVideo#PhotoshopTutorial#SpineTutorial#GameMakerTutorial#Spine#GameMaker#Photoshop
4 notes
·
View notes
Text
Star Wars Lasers in GameMaker: Studio
In lue of Star Wars: The Force Awakens, I made a script for GameMaker: Studio, allowing you to draw very customizable lasers like those seen in the Star Wars universer. Now I have written this small, explanation post about it. The script is used like this:
sw_laser(x1,y1,x2,y2,w1,w2,c1,c2,a1,a2,a3);
And could look something like this:
You draw the laser as a line going from (x1,y1) to (x2,y2). You then select a inner width (w1) of the normally white, opaque part of the laser. You can, however, change the color of the core (c1) and the opacity (a1) should you like it. Then you set the width of the glow (w2), the color of it (c2), the alpha where the core and the glow meets (a2), and the alpha at the end of the glow (a3). A regular, blue laser could thus look something like this:
sw_laser(x,y,x,y-100,16,8,c_white,c_blue,1,1,0);
The fully drawn laser will in this case be vertically straight, with a length of 100+16+8+8 = 132 from end of glow to end of glow, and a width of 16+8+8 = 32. You see, the core width is the full width of the core, but the glow width is existing on both sides of the core, thus you need to count it twice.
First thing that happens in the script, is simply assigning the arguments to variables, and making a couple of our own:
var _x1, _y1, _x2, _y2, _w1, _w2, _c1, _c2, _c3, _a1, _a2, _a3, _s, _amt; _x1 = argument0; _y1 = argument1; _x2 = argument2; _y2 = argument3; _w1 = argument4; _w2 = argument5; _c1 = argument6; _c3 = argument7; _c2 = merge_color(_c1,_c3,.1); _a1 = argument8; _a2 = argument9; _a3 = argument10; _dir = point_direction(_x1,_y1,_x2,_y2); _s = max(1,ceil((_w1+_w2*2)/16)); _amt = 90/_s;
As you can see, we make a third color, which is a slight mix of our two other colors. We do this simply so that the core is not completely white all the way through, which is a personal preference of mine. Secondly we make the variable _dir, which is the direction of the laser beam going from (x1,y1) to (x2,y2). Thirdly _s and _amt is simply to calculate how smoothly we need to draw the laser, depending on the size of it - the bigger it is, the more points we need to draw to make it smooth-looking.
Then, using primitive-drawing and a series of for-loops, we first draw the core of the laser:
//Inner draw_primitive_begin(pr_trianglestrip); { for(var i=0;i<=_s;i++){ draw_vertex_color(_x1+lengthdir_x(_w1/2,_dir+180+i*_amt),_y1+lengthdir_y(_w1/2,_dir+180+i*_amt),_c2,_a2); draw_vertex_color(_x1,_y1,_c1,_a1); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x2+lengthdir_x(_w1/2,_dir-90+i*_amt),_y2+lengthdir_y(_w1/2,_dir-90+i*_amt),_c2,_a2); draw_vertex_color(_x2,_y2,_c1,_a1); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x2+lengthdir_x(_w1/2,_dir+i*_amt),_y2+lengthdir_y(_w1/2,_dir+i*_amt),_c2,_a2); draw_vertex_color(_x2,_y2,_c1,_a1); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x1+lengthdir_x(_w1/2,_dir+180-90+i*_amt),_y1+lengthdir_y(_w1/2,_dir+180-90+i*_amt),_c2,_a2); draw_vertex_color(_x1,_y1,_c1,_a1); } //End draw_primitive_end(); }
The four loops are the four “corners” of the laser.
Afterwards we draw the laser’s glow. This is done in the same manor - look how our mixed color _c2 is used as the meeting point of the core and the glow.
//Out draw_primitive_begin(pr_trianglestrip); { for(var i=0;i<=_s;i++){ draw_vertex_color(_x1+lengthdir_x(_w1/2+_w2,_dir+180+i*_amt),_y1+lengthdir_y(_w1/2+_w2,_dir+180+i*_amt),_c3,_a3); draw_vertex_color(_x1+lengthdir_x(_w1/2,_dir+180+i*_amt),_y1+lengthdir_y(_w1/2,_dir+180+i*_amt),_c2,_a2); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x2+lengthdir_x(_w1/2+_w2,_dir-90+i*_amt),_y2+lengthdir_y(_w1/2+_w2,_dir-90+i*_amt),_c3,_a3); draw_vertex_color(_x2+lengthdir_x(_w1/2,_dir-90+i*_amt),_y2+lengthdir_y(_w1/2,_dir-90+i*_amt),_c2,_a2); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x2+lengthdir_x(_w1/2+_w2,_dir+i*_amt),_y2+lengthdir_y(_w1/2+_w2,_dir+i*_amt),_c3,_a3); draw_vertex_color(_x2+lengthdir_x(_w1/2,_dir+i*_amt),_y2+lengthdir_y(_w1/2,_dir+i*_amt),_c2,_a2); } for(var i=0;i<=_s;i++){ draw_vertex_color(_x1+lengthdir_x(_w1/2+_w2,_dir+180-90+i*_amt),_y1+lengthdir_y(_w1/2+_w2,_dir+180-90+i*_amt),_c3,_a3); draw_vertex_color(_x1+lengthdir_x(_w1/2,_dir+180-90+i*_amt),_y1+lengthdir_y(_w1/2,_dir+180-90+i*_amt),_c2,_a2); }
//End draw_primitive_end(); }
And that makes the whole laser! Hope you find the script useful, and that you are learning something from it. Feel free to tweet me how and where you are using it at @blesshaygaming
You can either just copy all the code from this post, or you can download it - and a lot more, cool scripts - here or on the GameMaker Marketplace.
1 note
·
View note
Text
Intersecting Lines in GameMaker: Studio
So, I just made a new script for GameMaker: Studio:
lines_intersect(x1,y1,x2,y2,x3,y3,x4,y4);
It is something I have wanted to do for a long while, but I had a hard time figuring out the math - after digging up the ol’ trigonometry everything fell into place though.
The x1, y1, x2 and y2 arguments are the two points of the primary line, and x3, y3, x4 and y4 of the secondary line. The script will return -1 if there is no intersection between the lines, or a value between 0 or 1 if there is an intersection. This value is telling where the intersection point is on the primary line, if you do something like this:
intersection_x = lerp(primary_line_x1,primary_line_x2,the_returned_value); intersection_y = lerp(primary_line_y1,primary_line_y2,the_returned_value);
The first part of the script, is simply initializing the variables, and finding the directions from the two lines’ starting points toward both their own end points and the other lines end points.
var _x1 = argument0, //Line 1, point 1 _y1 = argument1, _x2 = argument2, //Point 2 _y2 = argument3, _x3 = argument4, //Line 2, point 1 _y3 = argument5, _x4 = argument6, //Point 2 _y4 = argument7;
var _dir1 = point_direction(_x1,_y1,_x2,_y2), //From line 1, point 1 _dir2 = point_direction(_x1,_y1,_x3,_y3), _dir3 = point_direction(_x1,_y1,_x4,_y4), _dir4 = point_direction(_x3,_y3,_x4,_y4), //From line 2, point 1 _dir5 = point_direction(_x3,_y3,_x1,_y1), _dir6 = point_direction(_x3,_y3,_x2,_y2);
Then we can do our first test: if the secondary line’s end points are on the same side of the primary line (like, both end points are on the left of it, relative to the primary line’s direction), a collision is not possible.
//Compare line 1's directions var _diff1 = sign(angle_difference(_dir1,_dir2)), _diff2 = sign(angle_difference(_dir1,_dir3));
if abs(_diff1+_diff2) > 1 { return -1; }
If a collision is still possible, we can do the same check again, but this time from the secondary line; are the primary line’s end points on the same side of the secondary line?
//Compare line 2's directions _diff1 = sign(angle_difference(_dir4,_dir5)); _diff2 = sign(angle_difference(_dir4,_dir6));
if abs(_diff1+_diff2) > 1 { return -1; }
Now, this part is enough, if we just want to know if there is an intersection. UNLESS the two lines are following the exact same linear formula, in which case the two checks will not catch it. Now I will use the sinus relations to figure out where the two lines intersect.
First, we form a right triangle between the starting point of both of our lines, and an unknown spot on the primary line. This unknown spot is where the angle must be 90 degrees (_C). We already know the angle difference between the direction of our primary line, and the direction from this line’s starting point to the secondary line’s starting point (_A), and since a triangle’s corners are always 180 degrees in total, we can find the last angle (_B).
Using point_distance() we know the length of one side (_a), yet the other two sides (_b and _c) are unknown to us. However, these can easily be found using the knowledge, that in a right triangle, _a/sin(_A) = _b/sin(_B) = _c/sin(_C), also known as the sinus relations.
We now store the value of _b in another variable (_l), since this is “half the way” to the intersection point, and we need to change _b in a moment.
//Calculate triangle formed in the lines var _C = 90, _A = (_dir2-_dir1), _B = 180-_A-_C, _c = point_distance(_x1,_y1,_x3,_y3), _b, _c, _l; _a = _c/dsin(_C)*dsin(_A); _b = _c/dsin(_C)*dsin(_B);
_l = _b;
We now need to change the right triangle, to figure out the rest of the amount.
This new triangle will share the same _a side, and still follow the primary line, meaning _C will still be 90. Knowing the direction of our secondary line, we can easily find the angle _B, and since we then know two angles, we can find the third one (_A) just as before.
_B = (_dir4-_dir1)-90; _A = 180-_B-_C;
Now we solve the calculations for a moment, since there is a chance that sin(_A) is 0, in which case our further calculations will fail, since you cannot divide with 0. Good thing is that sin(_A) will return 0 when the line is following the same linear formula, and the lines are laying on top of each other. This means they are not colliding on one, single point, and we need to decide what the script then should return. I chose to return the mean of the colliding part.
if dsin(_A) == 0 { var _x, _y; _x = mean(max(min(_x1,_x2),min(_x3,_x4)),min(max(_x1,_x2),max(_x3,_x4))); _y = mean(max(min(_y1,_y2),min(_y3,_y4)),min(max(_y1,_y2),max(_y3,_y4))); _l = point_distance(_x1,_y1,_x,_y); return _l/point_distance(_x1,_y1,_x2,_y2); }
If this was not the case, we can continue our calculations. We need to calculate the new _b, and add it to our _l variable, the old _b value. Like in the code above, we then need to divide it with the length of our primary line, to get the value to return. And then we are done!
_b = _a/dsin(_A)*dsin(_B);
_l += _b;
//Find the scalar of line 1 to the collision point var _sc = _l/point_distance(_x1,_y1,_x2,_y2); return _sc;
The script has been uploaded as part of my script collection, which can be found both on The GameMaker Marketplace and in my Gumroad store.
Hope you enjoyed! Simon
Ps. I plan on going through more stuff in blog form, since it is faster than doing videos all the time, so hopefully it can mean more content for you guys.
Please do share this, and also let me know if this stuff is usable for you at all, since I see no reason to spit out more blog posts, if they are not useful.
I plan on mostly doing blog post for smaller scripts, or changes to my more organic engines, like The Cardboard Engine, also available both on The Marketplace and on Gumroad.
1 note
·
View note