Windows Warp3D - Beta Version Out Now!

S

Sam (Deleted User)

Guest
Hi everyone, I've got a quick update, I've made a few improvements to the engine while porting it to gms2.3:

Improved render batching system (slightly faster than before)

Batches are handled separately now,
(a "batch" struct is returned from "batch_create(list of instances)"
instead of only one global batch being possible,
so this allows say security cameras rendering a room the other side of the level in full detail.

Implemented a simple type of visibility culling using "zones"
You create rectangular zones for each say room or small area of the level,
and other zones nearby are flagged as "visible from that zone",
which, in the level init process a whole set of lists are created for instances
visible from each zone in the level, which cuts down draw calls a ton.

Also combined the zones into the collision system for dynamic instances (like the player, enemies, projectiles and anything else that actively checks for collision with things) in which instances only check collision in the zone they're currently in.
While testing collision with static instances(like items, ammo pickups etc.) and the precise triangles of the level model are still handled in the grid like before, as it's the fastest way.

Converted the entire gui to structs instead of instances. Before every button or thing you could click on was a separate instance of obj_gui, as it was way too complicated to do with arrays before, but I was able to convert them to structs in about 2 hours, just had to change a few instance_exists calls and other things where they had to be instances.

Made a few improvements to the model editor and the way "tools" are handled, which has made it easier to create new tools, so the list of tools should keep growing as time goes on.


I think that's it for now, I've mainly been refining all the code and thinking about it being a rock solid system, that's also easy to add things to. I'm very happy with how everything's going anyway.

Also, the new version is almost ready for beta testing. I'm going to invite a handful of people to beta test it, people that I trust and have spoke to before and some that've given constant support throughout:

@Amon
@Micah_DS
@TheSnidr
@JaimitoEs
@RujiK
@Yal
@Apapappa
@FoxyOfJungle
@Samuel Venable
@Xor
@flyingsaucerinvasion

If any of you are interested in testing the beta version, it'd be a HUGEE help, it will help with all sorts of angles to do with other people besides myself using it. Also in return for your help I'll give you lifetime access to the engine for free(via buying it on the marketplace and I then send you the money back on paypal 😆)

In the meantime, I made a cool sky dome shader, it basically uses a flat texture(photoshop clouds in this case) and projects it onto a sphere, created by a single cube, where the position coords are passed to the pixel shader, then normalized to create a sphere out of pixels. Then all that's left is to do a calculation that uses the normal and create a uv, plus some uniforms to affect the curve and scale. It's similar to how the sky worked on Quake 1, but I added a couple of other things to stop it looking so warped around the horizon

I just now noticed you tagged me in this. Is there still a spot open for beta testing? Thanks for thinking of me!

Edit:
fails to read exe demo out now lol
 
Last edited by a moderator:

Joe Ellis

Member
I've got the ball rolling again! (literally)
This is the first time I've got any gameplay working since I moved to gms2.
I've been having more fun with ball physics and improved them quite a bit, and made a skate park to test it in:


I just love playing with those balls and watching them bash into each other, it brings me great pleasure seeing my dreams come true.

I've improved the collision system to be simpler to work with,
every collision function that uses precise collision (sphere - triangle) or (capsule - triangle)
uses this one function: normal_to_triangle(tri, x, y, z, sphere_radius)
Which returns a vec4, the first 3 being the normal to the intersection point on the triangle,
and the 4th being the distance from the instance position\centre of the sphere to the point on the triangle.
Then different collision functions can use this info in different ways.
So it's cut down a lot of collision code, now the ball's collision function looks like this:

GML:
function collision_sphere_triangle(l)//list of triangles (got from current grid cell)
{ 
    var i = 0, t, n, d;
    repeat l[0]
    {
    t = l[++i]
    n = normal_to_triangle(t, x, y, z, mask_radius)
    if n != 0
    && n[3] < mask_radius
    && dot_product_3d(xspeed, yspeed, zspeed, n[0], n[1], n[2]) <= 0 //check moving towards triangle
    {
    d = mask_radius - n[3] //get the distance from the edge of the sphere to the intersect point on the triangle
    x += n[0] * d //Push the instance back in the direction from the intersect point, at that distance so that the edge of the sphere becomes level with the triangle surface (or edge)
    y += n[1] * d
    z += n[2] * d
    if n[2] <= ground_nz //sort most upwards pointing triangle to be the "ground triangle" which can then be used to make the character rotate based on the angle of the triangle
    {
    ground = true
    ground_nz = n[2]
    ground_tri = t
    }
    instance_get_bbox_sphere() //update the instance's bbox
    }
    }
}
Instead of this: (before)

GML:
///collision_precise_ball(list of triangles)

var
l = argument0, i = 0, t, vx, vy, vz, d, nx, ny, nz;

repeat l[0]
{
t = l[++i]
if rectangle_in_rectangle(bbox_x1, bbox_y1, bbox_x2, bbox_y2, t[_tri.bbox_x1], t[_tri.bbox_y1], t[_tri.bbox_x2], t[_tri.bbox_y2])
&& dot_product_3d(xspeed, yspeed, zspeed, t[_tri.nx], t[_tri.ny], t[_tri.nz]) <= 0
{
vx = x - t[_tri.x1]
vy = y - t[_tri.y1]
vz = z - t[_tri.z1]
d = dot_product_3d(vx, vy, vz, t[_tri.nx], t[_tri.ny], t[_tri.nz])
if abs(d) < mask_radius
{
if dot_product_3d_normalised(vx, vy, vz, t[_tri.t1x], t[_tri.t1y], t[_tri.t1z]) <= 0
{
d = median(0, t[_tri.e1l], dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.e1x], t[_tri.e1y], t[_tri.e1z]))
nx = x - (t[_tri.x1] + (t[_tri.e1x] * d))
ny = y - (t[_tri.y1] + (t[_tri.e1y] * d))
nz = z - (t[_tri.z1] + (t[_tri.e1z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
if dot_product_3d_normalised(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.t2x], t[_tri.t2y], t[_tri.t2z]) <= 0
{
d = median(0, t[_tri.e2l], dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.e2x], t[_tri.e2y], t[_tri.e2z]))
nx = x - (t[_tri.x2] + (t[_tri.e2x] * d))
ny = y - (t[_tri.y2] + (t[_tri.e2y] * d))
nz = z - (t[_tri.z2] + (t[_tri.e2z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
if dot_product_3d_normalised(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.t3x], t[_tri.t3y], t[_tri.t3z]) <= 0
{
d = median(0, t[_tri.e3l], dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.e3x], t[_tri.e3y], t[_tri.e3z]))
nx = x - (t[_tri.x3] + (t[_tri.e3x] * d))
ny = y - (t[_tri.y3] + (t[_tri.e3y] * d))
nz = z - (t[_tri.z3] + (t[_tri.e3z] * d))
d = point_distance_3d(0, 0, 0, nx, ny, nz)
nx /= d
ny /= d
nz /= d
}
else
{
nx = t[_tri.nx]
ny = t[_tri.ny]
nz = t[_tri.nz]
}
}
}
if d < mask_radius
{
d = mask_radius - d
if nz <= slope_limit
{ground = true}
x += nx * d
y += ny * d
z += nz * d
instance_get_bbox_sphere()
}
}
}
}
So you can see it's a lot easier to see what the code does and how it handles it, and the complex calculations are out of sight and out of mind.

If you're wondering, this is the code for normal_to_triangle:

GML:
function normal_to_triangle(t, x, y, z, max_dist)
{
  
    var
    nx = t[_tri.nx],
    ny = t[_tri.ny],
    nz = t[_tri.nz],
    d = dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], nx, ny, nz);
    if abs(d) >= max_dist
    {return 0}

    if dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.t1x], t[_tri.t1y], t[_tri.t1z]) <= 0
    {
    if t[_tri.e1_concave]
    {return 0}
    d = median(0, t[_tri.e1l], dot_product_3d(x - t[_tri.x1], y - t[_tri.y1], z - t[_tri.z1], t[_tri.e1x], t[_tri.e1y], t[_tri.e1z]))
    nx = x - (t[_tri.x1] + (t[_tri.e1x] * d))
    ny = y - (t[_tri.y1] + (t[_tri.e1y] * d))
    nz = z - (t[_tri.z1] + (t[_tri.e1z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    if dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.t2x], t[_tri.t2y], t[_tri.t2z]) <= 0
    {
    if t[_tri.e2_concave]
    {return 0}
    d = median(0, t[_tri.e2l], dot_product_3d(x - t[_tri.x2], y - t[_tri.y2], z - t[_tri.z2], t[_tri.e2x], t[_tri.e2y], t[_tri.e2z]))
    nx = x - (t[_tri.x2] + (t[_tri.e2x] * d))
    ny = y - (t[_tri.y2] + (t[_tri.e2y] * d))
    nz = z - (t[_tri.z2] + (t[_tri.e2z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    if dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.t3x], t[_tri.t3y], t[_tri.t3z]) <= 0
    {
    if t[_tri.e3_concave]
    {return 0}
    d = median(0, t[_tri.e3l], dot_product_3d(x - t[_tri.x3], y - t[_tri.y3], z - t[_tri.z3], t[_tri.e3x], t[_tri.e3y], t[_tri.e3z]))
    nx = x - (t[_tri.x3] + (t[_tri.e3x] * d))
    ny = y - (t[_tri.y3] + (t[_tri.e3y] * d))
    nz = z - (t[_tri.z3] + (t[_tri.e3z] * d))
    d = point_distance_3d(0, 0, 0, nx, ny, nz)
    var r = 1 / d;
    return [nx * r, ny * r, nz * r, d]
    }

    return [nx, ny, nz, abs(d)]
}
It's so far the fastest method I've found, using "edge tangents", I actually came up with this concept completely by myself which I'm quite proud of, but obviously don't wanna seem like I'm bragging either. If you're interested in this method just message me, I'm happy to share any knowledge or stuff I use.

I've also improved the collision system by making it detect and flag convex\concave edges, so now in the normal_to_triangle function, collisions with concave edges are ignored, as it's impossible for a sphere to collide with them, it'll collide with the flat surface of the triangle before. So this has cut down calculations quite a bit, it no longer has to get the nearest point along a triangle's edge if it's concave.

I've also made a big improvement to how dynamic lights are handled, before in 1.4, I had to render the lighting separately to the main materials using additive blending, because directx9c couldn't use a for loop in a shader with a variable length, ei. you couldn't pass in a uniform integer telling it how many active lights there are. It lagged terribly, even if only one light was used. So I had to make it go up in bands of 8, every lighting shader had to have 4 versions (_x8, _x16, _x24, _x32), and it had to render all the vertex buffers a second time using the lighting shaders that were configured for their main shaders. So it was really complicated and annoying.
But this problem with the for loop no longer happens in directx 11, so only one shader is needed, the lighting no longer has to be rendered separately to the main materials and now it can just be done in the main shaders. (Or not if certain shaders don't use lighting (another advantage))

Also there is now a special node type for a light (Before they worked with instances, and was a bit pointless and wasteful cus most lights are static most of the time) So they're a separate type of object\node now(structs), and for dynamic lights, you'd use an instance that controls it\edits it's values. They can also be created, deleted, activated and deactivated during gameplay with the functions (light_create, light_activate etc.)

@beta testers, I'm gonna upload a new version to test in the next couple of days, and you'll be able to test out these new things, and a few other gameplay things\objects (depending how many I get working in next couple of days) :)
 
Last edited:

Joe Ellis

Member

Got materials working again in the 2.3 version. These are basically structs that can use a shader, inherit certain uniforms from it that're marked as "material uniforms", and then anything using that material will get rendered with the material's specific uniform values.
So you can write shaders with more custom variables for certain effects and basically things that you want to adjust per material, rather than globally that affects everything, like the sun direction or color.

I made a pbr shader a while ago shortly before I released the demo of the 1.4 version, I used it on the weapons mainly to make them look shiny. But I've added dynamic lighting to it this week, and also made a mapped version like the materials you can design on Substance etc, 5 different maps for roughness, metallic, attentuation, ambient occlusion and normalmap. I'm also gonna make a combined map version of this, where you can combine the roughness, metallic, attentuation and ambient occlusion maps into one rgba texture.

I really love the plain pbr without the maps cus you can make every kind of shading you need in the game with just one shader, even for cartoony or retro games.

This is just one eggzample of what you can do with materials though, basically anything you can think of writing in a shader you can make all the variables fully adjustable by setting them as material uniforms.
shd_portal for instance has uniforms for the two colors it blends between, the speed, and the number of rings.



Code:
///shd_portal(render_portal, color)

attribute vec3 in_Position;
attribute vec4 in_Color;

varying vec3 v_pos;
varying float v_merge;
varying float v_alpha;

void main()
{
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
    v_pos = vec3(gm_Matrices[MATRIX_WORLD] * vec4(in_Position, 1.0));
    v_merge = in_Color.r;
    v_alpha = in_Color.a;
}


varying vec3 v_pos;
varying float v_merge;
varying float v_alpha;

//Global Uniforms
uniform vec3 fog_color;
uniform float fog_start;
uniform float fog_length;
uniform vec3 view_pos;
uniform float time;

//Material Uniforms (Uniforms are marked as material uniforms by putting "//" followed by the default value)
uniform vec3 color1; //rgb(0, 255, 255)
uniform vec3 color2; //rgb(0, 0, 255)
uniform float speed; //1.0
uniform float num_rings; //3.0

void main()
{
    gl_FragColor = vec4(mix(color1, color2, mix(0.0, 1.0, abs((fract((v_merge * num_rings) + (time * speed)) - 0.5) * 2.0))), v_alpha);

    //Fog
    gl_FragColor.rgb = mix(gl_FragColor.rgb, fog_color,
    min(1.0, max(0.0, distance(view_pos, v_pos) - fog_start) * fog_length));
}
 
Last edited:

Joe Ellis

Member
I've got the spread function working with the mouse in the model editor. Basically where you point the mouse, it has a radius and all the vertices in that radius are affected by what you do, with a soft falloff based on how far they are away from the 3d position the mouse falls on the mesh. This has brought a whole new level to the model editor, sculpting is now possible, soft color painting and it works with several functions(from the function tab on the menubar) so if you repeat a certain function (ctrl F) it applies the function to all the vertices in the radius with soft falloff. So it's really good for things like smoothing the mesh, randomizing\erosion, deform functions, color & alpha functions. Here's a video showing some of the things, and I also threw in some skeletally animated terrain just for sh*ts and gigs (the weights of the bumps were painted with the weight tool using the new soft brush. I'm going to make a tutorial video about rigging\weighting a model soon)

A few other pics:

 

Joe Ellis

Member

I've got some more things working this week,

First is the ability to matrix transform the skeleton and animations, so if you matrix transform a model (scale, center it around zero, or rotate it so it's pointing forwards) the transformation gets applied to the skeleton and all the animation frames aswell.
It was really hard to figure out, or at least, I spent countless hours trying different things and working out ways to do it, but in the end I realized a pretty simple method. (Which always happens)
So each animation frame is a set of dual quaternions for each bone, and they're relative and move the bones from the base pose of the skeleton, so if you just apply another dual quaternion to them from the matrix transform it makes parts that didn't rotate at all rotate.
So I needed to rotate the rotations... like the axis they rotate around, but keep the amount of rotation the same.
But in the end, you just need to invert the rotation\transformation, apply it to the base pose of the skeleton, then multiply that result frame\set of dual quats with each animation frame. So that's a huge weight off my mind.

The second thing is that I've merged the 1st person player and 3rd person player into one, mainly cus it was really bugging me having 2 player objects and having horrible long names, where I just wanted obj_player. So I've combined all the controls and movement so that all styles of controls can be toggled, and the 1st person view happens by default if there isn't a camera instance active at the time. So you can switch between 3rd and 1st person during the game, and toggle control style from either mouselook with wasd, or the old style controls of the arrow keys and a z to look up and down. I've also added usb controller inputs, so if a controller is connected it will use that instead. I still need to set up custom key & button config and the in game option menu for setting them though..

3rd, I've improved the item and inventory system a lot. Items are now basically "global structs" and are saved as files and loaded when the engine starts up. They have basic settings like max amount you can hold, the model & sprite they use and their collect and use functions. Plus they can have custom variables, like for weapons or special things you use that need other variables to control how they do things. These work by assigning an object to them, in the same way that instances placed in the level do, and they use any variables you've defined for that object and are all adjustable per item.
A good example of this is for weapons, so if you've equipped a shotgun, the item's object is obj_gun, which has variables for fire_rate, damage, accuracy, scatter etc. and every different gun item can have different config with these variables. There's obj_melee too, which has things like attack_speed, range, damage, pushback_force.. and you can completely make any object and variables you want for any item. So I'm really happy with how versatile this system is. Any saved games also save all the items currently in the inventory.
Now I just need to make the player be able to equip things again like in the 1.4 demo. That will hopefully be next week.

Finally, I've almost got the game finalizing process sorted out. Which basically saves all of the game files into a single rom. Then when the game is compiled into an exe it loads the files from the rom instead of the individual files. The only part with this that I'm still puzzling over is the textures, cus I could glue them all together into a rom, but then they'd be really easy to rip out, so I'm considering working out how to make an automatic function that edits the project file and adds all of the textures into the project as sprites.

So overall, these new things I've got working were things that I've been trying to work out for months and have really been stressing me out, so I'm really satisfied that I've finally managed to sort them out.
 

Joe Ellis

Member
Hi everyone,
I've been inactive for a while, I've been in a "transitional period" or as some would say "a period of depression", I prefer the term "transitional period" as it sounds more positive but both are accurate. I have found the light and have seemed to have come through the other side of it (Which always happens unless thou kills one's self) and have a fresh new energy vibe that I'm loving so far.
I've started my actual game that I've been wanting to make for years: Silkinstein
He is the cute lovable, lurky and likabley creepy character that's been my avatar for several years, and I've been waiting to get the game engine sorted to make the actual game he's in.
So here is a very early prototype of him and the game:


At the minute I've just got him walking about, but the vibe is right, that's the important thing.
The enemies and plot revolve around "The mice with long yellow hair" which are crossbreeding plants and animals,
like spunions(spider onions), sludders(slug udders), toad dogs(self explanitory),
and Slunion (A slug onion) He's one of the bosses, he has anger\rage, similar to a big gorilla, but in slug onion form.
It will probably visually take on a more industrial theme as you get into the MWLYH's bases and generally will have a dream-like theme throughout. (Including the floating rhabi with the two kids at the top of the hill watching him and commenting on his every move) (I love my dreams)
There are a lot of other things, but I'll leave them till I've made some of them, I'm pretty excited about this game though, it should be a kind of cult game in years to come, if I get everything right, which I will if I focus on the important things like the "vibe" and kind of implementing & translating all the ideas from thoughts\dreams to digital form.. :D

I've made several other improvements and progressions with Warp3D aswell, but my progress was so slow the last few months cus I've been depressed and found it really hard to get energy and motivation to do anything. Any tiny thing felt like such a huge task and required so much effort. But it's passed now and I should get loads of stuff done for at least until next winter..
So hopefully Warp3D will actually be released before then :D
 
Awesome. How is your progress? Do you use external rendering ? (External OpenGL / DX context), what version of OGL, DX? Or everything is done using GM Studio?
Thanks.
 

Joe Ellis

Member
Thanks, everything is done with gm. I used a dll in the gms1.4 version to load textures with mipmaps, but gms2 can do that itself now.
The dll could also pass texture pointers into the vertex shader, which still can't be done in gms2. So I might bring that dll back at some point.

I'm still making good progress, just been fixing bugs so far this week though 😴
 

Joe Ellis

Member
Here's some improvements I've made the last couple of months



Texture blending, for terrain mainly, the material lets you choose 4 textures,
then you paint with the soft brush the rgba values, each component is used for the blending,
so you'd mainly use full red, green, blue, the alpha is a bit confusing though, you need to use black, and 0% alpha..



Geodesic\hex grid terrain. Kind of a nicer look than a grid of quads, plus holes are smoother.
I like doing manual uv shifting aswell to make the edges of the holes look cool.



Expensive functions now do a gradual loading window, I'm going to add a cancel button aswell, cus sometimes subdivision ends up taking over 5 minutes or is gonna crash the game entirely, cus subdividing multiplies the amount of faces by 4, so it can quite easily go over the poly limit.



A screenshot of Silkinstein looking groovy, and the HUD might look abit like this.



Nice smooth lighting using dynamic lights, they can really add a lot of extra smoothness to ambient shading. The scene without them just had basic flat shading.



Concept art of Slunion, one of the bosses on Silkinstein, he's a slug onion. He has the temper of an angry gorilla, he slams and jumps about like one, but his arms and undercarriage are slug based, so he's pretty sticky aswell. He will be for sure interesting to make :)
He will be in similar style to Silkinstein, low poly ps1 character. (He has eyebrows even though he has no eyes)

I've also improved the general node handling system quite a lot, but it's not really much of a talking point.
I've basically just generalized it more, and made all nodes be handled in the same way with a few functions like node_create, node_delete, node_copy, node_paste.
Also I've made a kind of garbage collector, where anything that's deleted or instances destroyed get processed at the end of the step by the main engine object, which has made a lot of problems with instances or nodes not existing cus they've been destroyed\deleted while
code is asking about them. Everything has a variable "deleted" which can be checked before doing things if needed, and is better than "instance_exists" in most cases, plus some things might not be instances but checking ".deleted" works for all things.

Anyway, the engine since porting to 2.3 is pretty solid now, it's almost as solid as the 1.4 demo version.
Once it's at that stage I'm going to release it. Any extra things will be released as free "asset packs".
They'll be for specific things that not everyone will want like 1st person shooter ai, 3d platformer enemies, vehicle physics etc.
Then I haven't got to worry so much about everything being in the core version. People will be able to download the packs through the marketplace and just import them into their project.
 
D

Deleted member 16767

Guest
This is probably the most ambitious project on GMC. Welcome to the Raid Boss Team. Anyway, I have huge respect for Joe Ellis. If Joe Ellis was a raid boss, definately the hardest one to beat.
 

Joe Ellis

Member
I've worked out how to do vehicle physics and physics shapes with rotation speed and stuff.
This is one of the most complicated thing I've ever done, it's taken me a few months to work out how to do it.

It started with the idea of putting physics balls at each corner of a cuboid, well I actually did it in 2d first, so 4 circles at each corner of a rectangle.
Then each ball runs it's own physics & collision using circular\spherical collision, and they work out the movement speed and rotation by comparing it's new position and it's previous.

Then the balls are held together by a "body", which adds up all the ball's movement speeds and rotation and gets the average amounts.

The rotation wasn't that hard to do in 2d cus I could use angle_difference to get how much each ball had rotated from it's previous position, but in 3d it has to create a quaternion from the 2 vectors from the center of the body to the ball's previous position and current position.
So it gets 8 quaternions for each ball's relative rotation, adds them together and normalizes it to get the average rotation amount. This is then multiplied with the body's "rotation speed" quaternion to add it on and make it build up over time.
Then the rotation speed quat is converted to a matrix and multiplied with the body's absolute rotation matrix (To add the rotation speed onto the rotation of the model)
Then another matrix is made from the body's center position and the 2 are multiplied together to create the body's final world matrix, which is used when rendering the model.

The vehicle object has the following variables:

Rotation Damp - used to reduce\decelerate the rotation speed quat (default 0.97)
Rotation Accel - multiplies with the current rotation quat of the step, to reduce how much gets added to the rotation speed quat (default 0.97)
Accel - acceleration of the vehicle when driving forwards (default 0.5)
Decel - slows the vehicle (0.98)
Turn Accel - the speed of the tyres rotating left or right (default 0.25)
Turn Decel - the speed that the tyres return to the center if the player isn't turning (default 0.25)
Turn Speed - the max amount that is added to the rotation quat (default 0.13)
Wheel Radius - the radius of each physics ball (default 25)
Wheel Weight - default 2
Roof Weight - default 0.5
These are used to make the vehicle not topple over (heavier bottom than the roof)
Turn Threshold - used to reduce the turning amount if it's not moving fast enough (default 15) (this stops the vehicle being able to turn if it's still)
Thrust - Reduces speed based on how different the "forwards vector" and speed direction\vector are. (The forwards vector is got with matrix_transform_vertex(absolute_rotation_matrix, 1, 0, 0) and the speed vector is (x - prevx, y - prevy, z - prevz))
Overall Speed - A final multiplier to affect how much the vehicle moves (default 0.9) ei: x += xspeed * overall_speed; y += yspeed * overall_speed; z += zspeed * overall_speed; (This is useful for if you've got all the variables working nicely together but it looks too fast or slow, you can use it to reduce\increase the speed without altering the balance between the variables)

You can get quite a lot of different types of handling by adjusting all of these, which is cool.

Here's the code for it if anyone's interested:
GML:
function player_step_vehicle()
{

    //Get tyre grip based on how many of them are on the ground
    var grip = 0.25 * (wheel_bl.ground + wheel_br.ground + wheel_fl.ground + wheel_fr.ground);
 
    //Compare forward vector and speed to get friction
    var f = matrix_transform_vertex(rot_matrix, 1, 0, 0);
    var frict = lerp(thrust, 1, abs(dot_product_3d_normalized(f[0], f[1], f[2], x - ox, y - oy, z - oz)));

    //Accel
    if input_move_v != 0
    {
    if input_move_v < 0
    {input_move_v *= reverse_speed}

    var a = input_move_v * accel * frict * (0.5 + (grip * 0.5))
 
    xspeed += f[0] * a
    yspeed += f[1] * a
    zspeed += f[2] * a
    }

    //Jumping
    if keyboard_check_pressed(vk_space)
    {zspeed -= jump_speed}

    //Apply decel (Might change this so it only does it if not accelerating)
    xspeed *= decel
    yspeed *= decel
    zspeed *= decel

    //Apply friction
    if grip != 0
    {
    xspeed *= frict
    yspeed *= frict
    zspeed *= frict
    }
 
    //Clamp speed
    var d = point_distance_3d(0, 0, 0, xspeed, yspeed, zspeed);
    if d > max_speed
    {
    d = max_speed / d
    xspeed *= d
    yspeed *= d
    zspeed *= d
    }

    //Turning
    if input_move_h != 0
    {wheel_dir = clamp(wheel_dir + (wheel_accel * input_move_h), -1, 1)}
    else
    {wheel_dir *= 1 - wheel_decel}

    //Apply rotation from wheel dir
    if grip != 0
    {
    turn_amount = turn_speed * wheel_dir * frict * (0.5 + (grip * 0.5))
 
    var spd = point_distance_3d(0, 0, 0, x - ox, y - oy, z - oz);
 
    //Reduce if not moving fast
    if spd < turn_threshold
    {turn_amount *= spd / turn_threshold}
    }

    //Physics & Collision

    //Get old position
    ox = x
    oy = y
    oz = z
 
    var
    mid_x = x,
    mid_y = y,
    mid_z = z,
    i = 0,
    l = balls,
    r = wheel_radius,

    //Init relative quat
    qx = 0,
    qy = 0,
    qz = 0,
    qw = 1,
    mat = matrix,
    p, vx, vy, vz, cl, j, q, d,
    mx = 0, my = 0, mz = 0;

    //Get list of precise models the vehicle is intersecting
    cl = collision_bbox_get_list(obj_precise)

    repeat l[0]
    {

    //Scope into ball struct
    with l[++i]
    {

    //Store old position for collision checks
    ox = x
    oy = y
    oz = z

    //Get new pos from main matrix
    p = matrix_transform_vertex(mat, base_x, base_y, base_z)
    x = p[0]
    y = p[1]
    z = p[2]
 
    //Inherit speed from body & add gravity\weight
    xspeed = other.xspeed
    yspeed = other.yspeed
    zspeed = other.zspeed + (weight * !ground)
 
    //Store position before speed is applied
    //(for calculating the new speed after collision)
    cx = x
    cy = y
    cz = z

    //Apply speed
    x += xspeed
    y += yspeed
    z += zspeed

    //Get rotation base vector
    vx = x - mid_x
    vy = y - mid_y
    vz = z - mid_z
 
    //Update the ball's bbox
    //(has to be done manually cus it's a struct)
    bbox_left = x - r
    bbox_right = x + r
    bbox_top = y - r
    bbox_bottom = y + r
    bbox_z1 = z - r
    bbox_z2 = z + r
 
    ground = false
 
    if cl != undefined
    {
    j = 0
    repeat cl[0]
    {collision_wheel_precise(cl[++j])}
    }

    //Make a quaternion from the difference of the base vector and the new one after collision
    q = quat_vec_difference(vx, vy, vz, x - mid_x, y - mid_y, z - mid_z)
 
    //Add the quaternion to the total quaternion
    qx += q[0]
    qy += q[1]
    qz += q[2]
    qw += q[3]

    //Get new speed vector and add to the total speed
    mx += x - cx
    my += y - cy
    mz += z - cz

    }
    }

    //Divide total speed by number of balls to get the mean speed
    mx *= 0.125
    my *= 0.125
    mz *= 0.125
 
    //Clamp the mean speed
    d = point_distance_3d(0, 0, 0, mx, my, mz)
    if d > max_speed
    {
    d = max_speed / d
    mx *= d
    my *= d
    mz *= d
    }
 
    //Apply the mean speed to the body position multiplied by overall_speed
    x += mx * overall_speed
    y += my * overall_speed
    z += mz * overall_speed
    instance_update_bbox_z()
 
    //Set the mean speed as the new speed
    xspeed = mx
    yspeed = my
    zspeed = mz

    //Reduce the relative rotation quat
    qx *= rot_accel
    qy *= rot_accel
    qz *= rot_accel
    qw = lerp(1, qw, rot_accel)

    //Normalize the relative rotation quat
    d = 1 / sqrt(sqr(qx) + sqr(qy) + sqr(qz) + sqr(qw))
 
    //Multiply the relative rotation quat with the rotation speed quat
    q = quat_multiply(qrot_x, qrot_y, qrot_z, qrot_w, qx * d, qy * d, qz * d, qw * d)
 
    //Reduce\decelerate the rotation speed quat
    qx = q[0] * rot_speed
    qy = q[1] * rot_speed
    qz = q[2] * rot_speed
    qw = lerp(1, q[3], rot_speed)
 
    //Normalize the rotation speed quat
    d = 1 / sqrt(sqr(qx) + sqr(qy) + sqr(qz) + sqr(qw))
 
    //Get up vector
    var up = matrix_transform_vertex(rot_matrix, 0, 0, -1);
 
    //Create a quat for the turning
    q = quat_create(up[0], up[1], up[2], -turn_amount)

    //Multiply the quat with the rotation speed quat
    q = quat_multiply(qx * d, qy * d, qz * d, qw * d, q[0], q[1], q[2], q[3])
    qx = q[0]
    qy = q[1]
    qz = q[2]
    qw = q[3]
    //Normalize the rotation speed quat
    d = 1 / sqrt(sqr(qx) + sqr(qy) + sqr(qz) + sqr(qw))
    qrot_x = qx * d
    qrot_y = qy * d
    qrot_z = qz * d
    qrot_w = qw * d
 
    //Convert the rotation speed quat to a matrix and add to the absolute rotation matrix
    rot_matrix = matrix_multiply(rot_matrix, matrix_create_from_quat(qrot_x, qrot_y, qrot_z, qrot_w))
 
    //Apply the rotation matrix to the vehicle's world matrix
    matrix = matrix_multiply(rot_matrix, matrix_build(x, y, z, 0, 0, 0, scale, scale, scale))

}

I need to add another collision stage which detects whether the actual cuboid is intersecting any triangles, cus it's able to go through walls along the edges of it where the balls aren't there. (You can see that at 2:16 on the video)
So I think I'd need to do raycasting\lines intersecting triangles tests and push the vehicle out of walls.
I just hope it doesn't interfere with the physics. I think if I put the lines slightly behind the balls, the balls will always hit the triangle first, apart from in the case where they don't collide (at the edges in between them). Then it should only do the edge line push back as an emergency.
Or maybe I could make it use capsules along the edges instead of spheres...

Also for smaller objects colliding with the vehicle I need to make them check if they're intersecting the rotated cuboid. I'm not sure exactly how to do that yet. I think it'll involve applying the inverse of the rotation matrix to them, to rotate them into the model space of the vehicle, then it can just do a simple nearest_point_on_bbox and check if that's intersecting their sphere\capsule.

I might also change some of the ways it handles friction and grip. I'm not sure yet. I just got it working in a nice way yesterday and settled with that for now.
I was also thinking of making the shell of the car separate to the wheels. So the shell will have the wobbly rotation and the wheels will grip precisely to the ground by drawing them at their actual position after collision instead of using the body's rotation.
I think that would give an effect like on a lot of driving games.

You should also be able to make any kind of shape you want with this system, like rigid body physics. And if it's an irregular shape it'd topple over and it'd need spread across the bottom to stay up, like how stools and chairs work.
I haven't actually tested this in the 3d version yet, but the 2d one did so I'm hoping it will.
To make them easier to make I could make a function that builds the body out of the model's skeleton.. Capsule collision would be extra useful there.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
Very cool! So you can use this to connect spheres into more complex shapes? Are you going to make this into a general thing? If so, could you also modify it for making ragdolls?
 

Joe Ellis

Member
Thanks dudes!


@TheSnidr
Yeah I'm planning on making a few objects with this, one of them that's a general "physics object" or "obj_rigid_body" and it creates the spheres from the model it's using's bones. That way you can just create bones in the model wherever you want them to be.
And I could add "radius" & "weight" variables for bones.
I was thinking of doing the radius anyway for things like doing headshots and more precise bullet collisions, and the weight for ragdoll physics.

About ragdoll physics, I think it could use the same idea of each sphere doing it's own movement & collision, but I think there would be quite a lot of differences beside that.
I think it would be something like this:

1st stage: Each ball has it's own speed, apply speed to position, do collision pushback, set it's new speed by how much it's moved from previous position. (So this is the same)

2nd stage: all the balls would have to be constrained so that the lengths of the bones stay the same.
I'm not sure exactly how this'd be done.
One way could be to loop through them hierarchically from the root bone, and pull them back:
GML:
repeat num_bones
{
b = bones[++i]

repeat b.num_children
{
c = children[++j]

vx = c.x - b.x
vy = c.y - b.y
vz = c.z - b.z
l = c.length / point_distance_3d(0, 0, 0, vx, vy, vz)

c.x = b.x + (vx * l)
c.y = b.y + (vy * l)
c.z = b.z + (vz * l)

//maybe reduce bone's speed here too

}

}

I don't know if that would give the right effect though.
Maybe doing it backwards from end bones would be better..

Or another way could be with an average type process: (this'd give a stretchy effect though)
GML:
//Init new positions
repeat num_bones
{
with bones[++i]
{
new_x = 0
new_y = 0
new_z = 0
num_movements = 0

ox = x
oy = y
oz = z
}
}

//Pull connected bones back together
i = 0
repeat num_bones
{
b = bones[++i]
l = b.connected
j = 0
repeat c.num_connected
{
c = l[++j]

//Get vector
vx = c.x - b.x
vy = c.y - b.y
vz = c.z - b.z

//Get length
d = point_distance_3d(0, 0, 0, vx, vy, vz)

//Add pullback to connected bone
l = c.length / d
c.new_x = b.x + (vx * l)
c.new_y = b.y + (vy * l)
c.new_z = b.z + (vz * l)
c.num_movements += 1

//Maybe pull the bone towards the connected bone aswell (or do one or the other)
l = b.length / d
b.new_x = c.x - (vx * l)
b.new_y = c.y - (vy * l)
b.new_z = c.z - (vz * l)
b.num_movements += 1
}

}

//Set position to average of new positions
i = 0
repeat num_bones
{
with bones[++i]
{
x = new_x / num_movements
y = new_y / num_movements
z = new_z / num_movements

//Add speed from the movement (maybe)
xspeed += x - ox
yspeed += y - oy
zspeed += z - oz
}
}

It could maybe use all of those together, do the hierarchical ones after the stretchy one.
That might look the most realistic. I think it'd look more realistic than the hierarchical ones alone.

3rd stage, each ball would loop through balls it's connected to and get the average of their speeds (maybe including it's own).
Or maybe it could get the speeds by the length correcting process instead, that might be more realistic.

I think that would be the movement simulation working, but I'm not sure how it'd get the rotation speeds.
Maybe just get the vector difference of (parent pos to prev pos, parent pos to new pos).

Hmm, there's probably a few things I'm missing out at the minute.
It'd also need rotation constraints adding to certain bones, like the shoulders and spine to making sure the ragdoll doesn't get in some kind of impossible pose.

I'm interested to see how this'd work though now you've got me thinking about it haha :D

I'm gonna test out these ideas with a physics rope first, that shouldn't be too hard, and it should give me a good idea of what things it needs to do, and when.
 

Joe Ellis

Member
I just had an idea about writing the manual. Cus I've got some kind of mental block where every time I try to write something for it, my mind goes blank and I can't think where to begin with explaining a certain topic.
So I thought, cus I'm usually good at explaining things when people ask me, if any of you have any questions about using it, how certain parts work etc. you could you ask me here and I can use the answers to get a big chunk of the manual down, then I can fill in the gaps.

Also if any of you have got any advice on writing a manual that'd be really appreciated :)
 

Joe Ellis

Member
Yep, don't worry I've already sorted that out :D I'm using Helpsmith which can compile it as webhelp. So I'm assuming when it's viewed in chrome it can be translated. I don't know about other browsers cus I haven't used them. But I guess most can now.
 

Joe Ellis

Member
Here are some updates I've made the last few weeks

In short:
  • Removed custom collision system and built everything to work with gm's built in collision system.
  • Changed the way nodes are handled to be more general and versatile.
  • Added some cool features to nodes like linking, emitting light and the ability to create custom structs.
  • Improved file format, made error proof.
  • Worked out a fast way of doing indirect lightmapping.

And now an essay about them:

Collision System

The custom 3d collision system it used has been removed. This was basically 2 grids of everything (instances and precise triangles) that spread over the area of the level. But it was quite awkward to set up different objects doing different functions upon collision and wasn't\couldn't be as fast as gm's built it collision system.
So I changed it so instances use one of 2 mask sprites: mask_central(2x2 pixels with origin of 1, 1) or mask_topleft (1x1 pixels with origin of 0, 0) This way the bbox_left, right, top and bottom get updated automatically and the instances just have to update their bbox_z1(top\up) & bbox_z2(bottom\down) each step.
So now everything works with the built in collision functions and best of all, the collision events.
Now you can create collision events between objects and all you have to do is make sure it's also colliding along the z axis with
"if rectangle_in_rectangle(0, bbox_z1, 1, bbox_z2, 0, other.bbox_z1, 1, other.bbox_z2)" before any of the code for the event runs.
This has made everything with collision a lot more organized and easier to manage.
Before you had to assign\define specific functions to run when there is a collision with another object.
eg. object_define_collision(obj_item, item_collect)
And it this was pretty annoying to work with cus you'd always have to make a special function for that collision event.
But now you can put code right in the actual collision event and they're all contained inside each object so much easier to browse through.


Nodes

I've changed the way nodes(objects in the level) are handled to be a bit more general, and preset files can be a preset of any node type instead of just for instances.


Linking

I've made it so you can link nodes together. (By pressing L when the mouse is over them) And the links are displayed as lines between them. (This can be toggled off in the View menu)
So this could be used with triggers and things, like linking a switch to a door, so that when it's pulled it opens the door.
Or an invisible trigger in a doorway that when the player touches it it turns a light on and activates all the instances in that room.
You can just write a function for the trigger that loops through it's list of links and does different things to them depending on what they are. Like if it's a light and the light is off, turn it on. If it's an item and it's invisible, set it to visible. If it's an enemy, alert it and tell it
where the player is.
And you could do things like when an item is collected or an enemy is killed trigger something to happen elsewhere in the level like open a door or spawn something.

The level editor on rayman 1 (Rayman Designer) worked like this and it was a really easy and hands on way of dealing with triggers. Like you could actually see lines for what everything was linked to instead of it being hidden in their properties.


Custom Structs

I've added the ability to create static structs instead of instances in the level, so for any instance that uses a blank object with no events it can be a struct instead. The structs can have all the same properties as instances including having an object assigned to them in which case it will contain a set of variables that were defined for that object.

I don't know if I made it clear before but the variables for each object are defined in the event user 0 of each object and they're all executed once while the engine is starting up.
To define a variable, you just write one of the variable create functions which are all named "var_" + the name of the type you want it to be.
eg. an object's event user 0 could look like this:

//obj_projectile

var_real("Move Speed", 10)
var_real("Grav", 0)
var_preset("Explosion", "explosion_normal")
var_integer("Max Bounces", 3)
var_ratio("Bounce Scatter", 0.3)


Custom Save & Load Functions per object

I've added the option to define a custom save & load function for an object, so any instances or structs using it will run the custom functions when saving and loading it. (after the regular variables are saved)
So this should make it a lot easier to make your own types of things with specific file data
eg. dynamic\heightmap terrain, 3d voxel maps, sector maps(doom style) or something like the player instance having a nested array structure going on that can't be saved with the standard variable save functions.

I've also come up with a nice and easy way to handle each instance being able to have it's own inventory.
So for example a container, you could add an inventory to it with 3 crystals, 4 gems and 2 diamonds.
Then in it's destroy event you could make it spawn the inventory as collectible items.


Light emitters

Any instance or struct can have "emit_light" enabled, in which case it adds the light variables to it and they get updated automatically as they move about.
The light variables are:

light_active
light_x
light_y
light_z
light_color
light_brightness
light_radius

(The xyz values are relative, so they work like an offset)


Summary

With these additions, it doesn't really need the separate node types (lights, points and any other type I'd make in
the future (voxel maps, terrain etc.)) Now nodes are just instances or structs.


Presets

Presets can also work as "global structs", which can be used in a lot of ways. Such as inventory items, like different guns having different variables for fire_rate, damage, accuracy etc. and they can be useful in other cases like different enemy types sharing the same variables. They can all refer to their type struct instead of having identical copies of a set of variables.
This would also make it a lot easier to tweak these variables while testing and it'll update all instances using them instead of only being able to edit 1 instance at a time.


Postface

I think these changes together will make certain things a lot easier and less awkward to do. Most of them were made while I was trying to make certain example objects and something was really awkward.


File Format

The file format has been changed a bit and the way data is saved has been made more secure\error free.
The format before was able to crash in certain cases, like if you changed a variable type from a number to a string. A file saved before the change would still be using the number type and load the wrong type of data, and if the instance used it in it's update event (event user 1)
(which is executed when the level is loading) it could crash due to having the wrong data type for the variable.


Indirect Lightmapping

I worked out a nice and fast way of doing indirect lightmapping mostly on the gpu, without making it cast billions of light rays.
The way it does it is actually quite simple: (similar to light probing)

It uses a 1x1 surface to render to each pixel\texel on the lightmap at a time.
For each texel on the lightmap, it sets the view position to the position it is on the triangle's surface, pointing in the direction of the triangle's normal (+ random scatter). Then it simply renders the whole model so that it renders what that texel can "see". Which simulates light rays bouncing off the other triangles onto that spot on the lightmap.
Then the 1x1 surface is drawn onto the main lightmap surface at the correct position.

I thought this would be really slow at first cus of how it has to submit the whole model vbuffer 512x512(262144) times per pass. But because it's rendering to a 1x1 surface most of the geometry is culled before it reaches the pixel shader. So it can do it about 500 times per step without lagging.

From testing with a 512x512 lightmap, it takes roughly 10 seconds to perform 1 pass. Which is really fast considering what it's doing.
I think for a smooth\none grainy result it probably needs about 32 passes. (5 minutes) so you can just go and make another coffee while it's rendering. And for a final game quality lightmap with 255 passes it would take about 40 minutes.
I'll make a video of this soon once it's set up properly.
It should look the same as unreal at high res, and quake and other early lightmapped games at lower res.
You'll also have control over the scatter amount, curve balance between light and dark. And the lightmap gets saved as a png so you can edit it in an image program.
It also pads by n pixels when finished, by simply drawing itself across multiple times. Cus empty texels that aren't used by a triangle have 0 alpha.

This approach could be used for light probing aswell. And that'd be really fast cus each probe would only need about 30 samples.
So maybe it could be done in realtime ?
I'm not that interested in light probing to be fair but, maybe I will be if I can use this technique with them..
 

Joe Ellis

Member

New level editor features! (Basically all the new things I mentioned in my last post, but actually fully integrated and editable, ei. finished)

Linking entities together.
Then the entities have a list of other ones they're linked to, so you can use them in any way you want. Say when a switch is pulled, it can activate every entity it's linked to.
Or even a white walker style kill the leader and all others die. ANYTHING, HECK!
*cringes inside slightly, then thinks, no actually, everyone gets my sense of humor by now, and if they don't, they will when they read this.
Hmm, maybe I overreacted... Anyway! >*

Now, any entity can have an inventory.
So for a crate, you put things in it's inventory, then when you blow it up it can spawn them.

The items in the inventori (plural) are simply a collection of "presets".
Presets are basically entities saved to a file, and are loaded whenever referenced or searched for.
So they're like global entities\structs that just hold a bunch of variables, and can be accessed in the same way as an instance or a struct in the level. (I mean, super useful yah?)

So they can be used for anything really, like a weapon:
has variables for:
Damage
Range
Speed
if it's a gun, Fire Rate
Projectile that it fires (which is also a preset)

So you can choose the type of projectile(preset) it fires. So it could fire guitar amps if you wanted it to. (They would be pretty deadly to be fair too)
And they could explode when they hit something and spawn another preset of say a camel, with 3 humps, and sometimes the middle hump emits another preset of road cones...

So in summary, presets are a way to make structs that're there globally with any variables you want at all (by setting their object(gm object) with defined variables) and every instance and struct can read their variables.


Also I've added an "Emit Light" bool for entities, which makes them.. emit light.
Instead of before when lights where a separate type of "node\thing" in the level.
Now everything in the level is just "Entities" instead.

Entities are structs, and if you set them to be "active"(by default they are), when the game is played they create an instance that's like a duplicate of them (at first) with all the same variables. But then obviously the instances run their events and the variables' values change. But if you ever need to reset them, the entity has all the original variable values still.. So It's a good way keeping original states of everything and also being able to keep track of the instances and deactivate\destroy them if you want, while being able to reactivate them later. (I'm currently thinking about the "original state" thing I just said and when you deactivate an entity.. currently it saves all it's current variables to the entity struct it belongs to.. but then the original state is lost.. So I might have some re thinking to do in that department.)

I'm really happy with these new changes I've made though. It feels like a really solid and reliable game engine now :)
 

Joe Ellis

Member
Update-

I've made a huge amount of progress the last couple of weeks. I think most of the program is set in stone, and I've been working on a lot of template objects.

Here's a a list:

Moving platforms and objects(can be used for doors and bridges) that can move, rotate, and a orbit around a certain point.
Collision works with all of these as they're rotating, even objects with precise collision (per triangle) and objects like the player, if they're standing on them get moved and rotated with them.

Triggers, that when the player collides with them, (or if the action key\button is required(like for a switch) or a certain key) they can affect other objects. This is done with ev_user15, to save having loads of functions needed for each interaction.. they're all inside the objects.
The triggers can also trigger other triggers, and each one can have a delay, like a timer before it triggers everything, so this means you can link triggers together to make sequences of things. Like a bunch of explosions happening at different places and spaced out time wise.. and a million other things.

I've also made "one-way linking", cus before when you linked entities together they would both get linked to each other, and there's a lot of cases where you'd want it to work in a one-way way..
Like for triggers, you'd have the 1st trigger that when the player touches it, it triggers the other ones, but if those other ones were to trigger other triggers they'd be linked to the 1st trigger that triggered them, and then you'd need to add a condition in the code to not trigger that 1st one that triggered it. At first I thought each trigger could have a variable "parent" that's the id of the 1st trigger. But then I realized it would make things way simpler if things could be linked to other things, without the things it's linked to being linked to them.
So now you can enable "one-way linking" on the options menu and then, if you link one object to another, only the 1st object will be linked to it. Sorry if this sounds confusing, it actually isn't when you use it.. and I've made it so the link lines between them have arrow heads pointing at them, so it's easy to tell if something is linked to something but the 2nd something isn't linked to the 1st.

Collision improvements. Now everything works with a universal set of functions that handles each collisions mask type interacting with solid objects. Currently only sphere and capsule masks work for dynamic objects (like the player, characters, moving things), but these are the main 2 you'd want for most objects anyway.
The solid objects can have any mask type though (Cube, cuboid, cylinder, sphere, capsule, or precise) and everything collides with them as you'd expect.
And the solid objects can move and rotate (even with precise per triangle mask) and the other objects collide with them properly, and if they're sitting on top of them, they get moved and rotated.
(I really need to make a video of this to show how cool it is)

I made a new variable type, array. Mainly so the player can have an ammo array that's a list of amounts of each ammo type, and you can edit that in the editor. But it will probably be useful in a lot of other sitches.. and I've made it so you can link the variable to a list of names,
so for the ammo array, when you edit it it says Bullets, shells, cells etc. next to each slot where you set the amount.

Ai handling for npc's, (all enemies and NPC's with different ai's, but they're all in the "actor" group, and they function the same as the player object (with states for the physical state it's in, like standing, jumping, attacking etc. but instead of responding to the keyboard\controller inputs, the inputs are set by their ai script. So this has made ai programming a lot simpler, all the ai functions don't have anything for the actual movement in them, they just set "input_move_dir" to a certain angle which makes them walk to a certain place.
I've also made 2 objects for checking sight(whether they can see the player) and whether they can walk to a certain spot. So the ai scripts fire these checkers off before making a decision... The sight checkers return false if they hit a wall before they get to the player and the move checkers return false if they hit a wall or fall off a ledge.


I've done a few other things like an inventory menu and improved the player's handling a bit so it slows down when it turns.. But I need to make a video to show all of this stuff properly.. I'm still testing a few things though so it might be a week or 2 before I make the video with everything in it.
 
Last edited:

Joe Ellis

Member

It's not the polished feature bonanza video I've had planned but it has got most of the new things I was talking about. Most impressive is a moving\rotating model that the player can stand on and it rotates and moves the player with it, with a full precise collision mode (per triangle) This has been something I've always wanted to make, and it was mostly just a dream, cus it's not even that useful, like most games don't have this, or if they do it's only on 1 or 2 things in the whole game. But it's pretty cool that it works, and there's no glitchyness at all as well which I'm really glad about. Cus pretty much every game I've played that has this it's been glitchy, like the character having jagged movement as it moves with it or something going wrong where it doesn't rotate with it properly. So I'm pretty pleased about this. Then there's a clip of where I've so far attempted to make a path based camera.. Like on crash bandicoot really (is what I'm hoping for) and several other games where the camera is like choreographed.. It doesn't look that great at the minute. It's partly cus each "camera path point" has adjustable yaw, pitch, distance and position.. So I think that kind of thing will take hours of tweaking in any game. Also the transitions, or the way it navigates and moves along the points is probably not the best currently, but I like how it's going so far, and it's definitely the camera style I want to use for Silkinstein, cus it immediately adds a more personal and immersive feel to the game. But it will be crazy tweak-wise to get it exactly how I want.. That's good though really :) I'll always have something to do even if I'm not feeling creative. You don't need creativity to make the camera not look weird.. Or do you?? In the middle of the video, I made several moving platforms with mushrooms all with precise collision mode, and it lags terribly! It actually doesn't when I wasn't recording, but as soon as the recorder kicked in it couldn't handle it.. So that's probably what it'll be like in a full game with everything else going on. But when the big "level model" was moving it was fine. I think this is cus the amount of triangles are more spaced out so it only has to deal with 10 or so at a time, whereas the mushroom model was that small and had about 100 triangles, it had to check collision with all of them at once. So probably this kind of thing should be done with either large moving things, or very low poly small things.. Also a cool little feature, I don't know if you noticed.. Ground tilting! So if he's walking on a slope he tilts at the slope's angle. This was quite a cool little trick I worked out using quaternions and the quaternion_vector_difference function I found while I was making the vehicle physics thing.. It's pretty cool cus you basically get the normal of the ground he's standing on, and you can make a quaternion from that normal which is like the "tilt" rotation. And you can get it just by calling that quat_vec_difference function with (0, 0, -1, ground_nx, ground_ny, ground_nz) so it gets the difference between the ground's angle and straight upwards, then you just turn this into a rotation matrix and apply it to the character's rotation matrix. (The only awkward thing is you have to apply it to the character's matrix before the translation is applied, so you have to first make the character's rotation matrix based on which way it's facing(yaw) then multiply it with the "tilt" rotation matrix, then multiply it with the translation matrix. But, it's not really that performance heavy and it's worth it. I think it's added a lot to the immersiveness of the game. Then at the end of the video I'm just showing the animator, I made the bones have yaw, pitch and roll so adjusting them is easier. Before they just worked with the arrow keys.. and the pitch axis depended on the camera angle (Ughh no) I personally like how the rotations work with how it is now, but I need to test to see if they're that useful in most scenarios, cus Silkinstein's animation is so simple, he just has one bone for each floating part of his body, so the way it currently works is great for that, but I think for most characters (with limbs) it would be more convenient for them to rotate around their parent bone.. But I think I'm gonna add a "Bone Toolbar" with settings for this, so choosing whether to rotate around the parent or just itself, and a few other things like the axies the yaw pitch and roll settings use.. It should be ok ;) PS ignore the spot shadow being not depth sorted, this was mainly a test of the "over_ground_z" variable and I ended up snazzing it into a spot shadow instead of just a dot.. The real one will be an actual sprite.
 

Joe Ellis

Member
So, a quick progress report
I've got back into programming this week, after a few months of inactivity\depression, but I've come back really stong! (strong)
I've got loads of things done, most of the example game objects are set in stone, I've made some good improvements to the model editor, fixed alot of bugs, and generally cleaned things up ready to beta release it, which I'm planning on doing next week.

I some detail, I've improved the auto-rig function for the skeleton rigging process and it works really well, I never got it to work that well before, it didn't do it properly, but the "right way to do it" suddenly hit me and then it worked perfectly. (like other programs).
I also made a mirror option for the soft brush mouse thing, and it works with all the tools and functions, like weight painting, sculpting, color painting. I guess this explains it pretty clearly:

Before:


After:


Also when you're painting weights for the bones, you can select the mirror on the Y axis, then it'll apply it to the front and back of a limb, which is really handy, I kept getting annoyed about that.

However the best thing I've done is the lightmapping engine! I've got it to work with real indirect lighting, like the light rays bouncing around and lighting up parts that're in shadow. and tbh I'm blown away by the results:








 
Last edited:

Joe Ellis

Member






I've only tested it with this simple rectangle room so far, cus the uv mapping has to be done manually, spreading each part out onto a sheet like this:


But I've worked out an automatic function and I should get it working in a few days.
So I guess making a quake\bsp style game is now on the cards, plus more, I suppose this type of lightmapping is on par with unreal and cryengine...

Also on the example game, I've made state machine characters for the player and enemies, they both work with the same system I've called an "actor" and they all work with the same set of code, that handles all the things characters need: animation, movement, collision etc.
They have the "input function" which is either the player input from the controls (keyboard or usb controller) or the enemies' ai function,
which sets "input variables" like input_move_dir (the direction to move), input_jump, input_attack...
Then the physical "state" function is executed (ground, air, water, attack, hurt, death), which uses the input variables to move, jump, attack etc.
and sets all the right animations for what it's doing. So they're literally controlled by the input variables. This means you can code something to control an actor really easily, just by setting the simple input variables, and it'll behave based on them. So it's made ai scripts\states really easy to code, cus you don't have to deal with any of the actual movement or other variables, the ai scripts simply set the input variables. So you just tell them what to do, kind of like making it press the buttons you'd press if you were controlling them. So if you want an actor to walk towards the player you just do: input_move_dir = point_direction(x, y, player.x, player.y).
Then something like: if point_distance(x, y, player.x, player.y) < 300 {input_move_dir = undefined} Which makes it stop. So then you've got a character that follows the player!
If it was an enemy maybe do something like: if point_distance(x, y, player.x, player.y) < weapon.attack_radius {input_attack = true}
and that's all that's needed from the ai script, the physical state machine will do the rest :D
 
Last edited:

Kentae

Member
Nice! ^^
Thought for a while there that you'd given up on this, So it's very good to see you back at it! ^^
Keep up the good work! :)

PS: "We are the Rubber People! Bow before us and bask in the light of our magnifisence!" was my thoughts when I saw that rubber fellow in the video, love it! :D
 

Joe Ellis

Member
I've been working all week fixing bugs and getting the example game stuff working. I've been trying to get it ready to beta release for 2 weeks now! There's still a few things left I need to do, but hopefully next week I'll get them all done ! :banana:👍

This is my to do list:
  • test guns (firing projectiles)
  • make automatic guns (machinegun - skip back to start frame when reaches end frame)
  • test main menu (saving\loading games)
  • get materials working again (Not currently working!)
  • make animation for eggzample firing guns
So you can see there are only 5 main things left!

 
S

Sam (Deleted User)

Guest
your work is getting more and more impressive every time i look at this thread.
 

Joe Ellis

Member
@Samuel Venable thank you my friend. I sure do keep developing and moving forward, and this is reflected in my work.
None arrogant version:
I keep getting better at making things simple, coming up with nice and easy ways to do things. Each part of the example\eggzample game I've focused on making it really easy to understand. So the majority of all the objects in the example game work in the same way as a normal 2d game project would work, even with the collision events. So I'm hoping it won't be too hard for people who're used to gamemaker to pick up and start working with the engine.
 

Joe Ellis

Member
@Garström OMG are you one of the 10 people who bought it!? I'm so sorry and I hope all your dreams will be answered! HAHAHA
I released it way too early. I didn't actually know what I was doing to be fair on me. haha
Yeah I do feel bad for whoever bought it and then couldn't do anything with it..
At the time, it was a beta release (Early access) Just to be able to test parts of it out before it's finished...
But it seems crazy looking back.. Cus of the amount it's developed since that time.
 
Last edited:

Garström

Member
@Garström OMG are you one of the 10 people who bought it!? I'm so sorry and I hope all your dreams will be answered! HAHAHA
I released it way too early. I didn't actually know what I was doing to be fair on me. haha
No worries, I knew it would take time to get it out. I was a bit worried when you keept adding more and more features. But it looks like it panned out in the end.

Also yes I'm one out of ten. I think that I got the old version on one of my hard drive.
 

Joe Ellis

Member
Cool, it's hard to say anything really, it was so long ago. I remember at the time it (what I was doing) was good, but the beta was a very "test it but don't expect anything to work" kind of deal. I guess that's what early access is for.. You got a glimpse of what the project was like for me at the time. But it's obvious that it needs further development. Now (currently) it's literally a god version of that. pretty much everything works, there's just a few things I need to fix before I consider it fit for release. At the end of the day if I release without these last few things it would just look lazy and like I didn't care, and it's only gonna take a few days to make them

It is really annoying though, I wish time could freeze while I'm doing it, cus it takes a ridiculous amount of time. Most days are over before I've even done a poo (so to speak)
Still, it's like "Where did that time go?" "Oh, I was working stuff out"
The good thing is, I (in my opinion) I get great results from what I do\work out. But sometimes it takes like 5 weeks to work out a "really good" way of doing something.
I don't know what you call it, whether I'm a perfectionist, or just someone who knows what's good, and always tries to make things of high standard.. I don't know.. I don't even care at this point, I just want to get my program released.. It's what I wake up with every morning at the minute.. "Get Warp3D released!"
 
Last edited:

Joe Ellis

Member
I'm sorry I still haven't released the beta version! I kept finding bugs and it's taken a lot of time to fix them... I also started adding new things cus the motivation I gained from releasing the beta version has given me a huge kick up the arse and I've fine tuned a lot of the stuff.
I really hope that next week I will prepare it for the beta release and it should be uploaded by next friday !!! (I just really hope this turns out to be true)
Last time I had a to do list of 5 things, I did them and then the list grew into 30 things:

So that's a quick progress report.
I've also made Zoneular culling, where you make cuboid "zones" then link them together, for each zone, link any other zones that are visible from that zone. Then I've made a system where the camera gets which zone it's in (global.view_zone), then only the entities that're in the zones that global.view_zone is linked to are rendered. And it is a massive life saver for performance. It can also deactivate and activate entities in the same way, which makes them practically have no performance hit while they're inactive, so this is really good for games with a lot of objects. (Obviously, I kind of don't know why I said that)

Also I found this really cool program "Noesis" which can load levels from loads of old shooter games and export them as obj or fbx files. So I did this with Quake! and it even exports the lightmap (if you use fbx), so this was a great way to test the lightmap rendering procedure, and also the thing where the player gets the current ground it's standing on and reads the lightmap at that position. So I've made it work like a lot of old games that used lightmaps where the character goes darker when it steps in shadow areas. (COOL)
Noesis is really cool though, it can convert levels from: Doom 1, 2 & 3, Any Build game (duke nukem 3d, whichaven, etc.) Hexen 1 & 2, Quake 1, 2 & 3. And probably a few more..
So this means you can use something like build or a doom or quake level editor if you want and easily export it into gamemaker and warp3d.

Also this has made me improve the obj file importer a lot, before it just imported all the geometry into one mesh and didn't import any of the textures. But now it loads each mesh separately and assigns the textures specified for each one. (As long as you put the textures into the textures folder in the project datafiles folder) So the quake level you'll see on the video was literally imported from the obj file and that's what it looked like as soon it was imported.
I also made a special function for importing the lightmap uvs, cus obj files can only have one set of uvs (I think?) So I made a thing where if you split the fbx model into 2 versions (in blender or something) export 2 obj models each with identical vertices, but different uvs for each of the 2 channels. Then Warp3D imports the main obj model you pick with the standard textures and uvs, then if there is a file with the same name but with " Lightmap" on the end of it, it'll automatically load that and load the uvs for the lightmap into the same model.

I've also made all the shooting weapons (wait, they're called guns)
Pistol, Shotgun, Uzi, Machinegun, Grenade Launcher, Plasmagun, Bazooka, Flamethrower, Harpoon Gun

Also potions for invincibility and super jump (Both have timers that countdown for how long they last (like 20 seconds))

Made projectiles be able to have gravity (0 by default) but you can set it to something and it makes them act like arrows that arch.

Made a "muzzle flash" sprite for guns, and it kind of makes the guns firing look a lot more convincing.

Also made the gun models work with bones, so you add 2 bones to it, the handle and the muzzle(where the projectiles are spawned)
So this has made it a lot easier to get them to look right when attached to a character. Before you had to tweak all the position and rotation coords manually to make the gun be in the right place, but now they automatically attach to the character's hand bone by their handle bone. I also made a function "Test attached" which you can use in the model editor to test the gun models are in the right place, so you can tweak them and it also works with the character being in certain animation frames, so you can really get the gun to be at the right angle when the character's arm is fully aiming.. I'll have to make a video of this to explain it better.

I've also made switching between 1st and 3rd person, so the 2 player objects (obj_player_fps and obj_player_3rd_person) are now just "obj_player" and all the same stuff works in either mode. (Complete with mouse looking (Cus before it only had keyboard controls))

I think I might've done a few other new things, but can't remember them at the moment. As you can see, I've been really motivated and got a lot done.. I just really hope next week I can focus on literally exporting the project and uploading it..

Oh I've also started writing the manual! I've got about 10 pages done so far, which explain most of the basic stuff you need to know.
It'll take me a while longer to finish it, but I'll upload it in it's current state when I upload the beta version, I'll put a link to both on the OP of this thread, and I'll reupload\update it whenever I add anything to it.

I'm pretty tired after writing all this, maybe you are from reading it too 🙃

Anyway, Eggzample had fun this week:

Oh yeah, after watching the video it reminded me I made auto aiming for the player, where it finds the enemy that's closest direction to the direction it's facing, but it doesn't work properly you see on the video he keeps aiming\firing downwards into the floor cus there's an enemy below under the bridge, so I need to make the aiming 3d, cus it currently does it with angle_difference and point_direction to each enemy and gets the smallest difference, so I need to make it also work on the vertical axis aswell, then that problem'll be fixed.

It's pretty cool seeing eggzample walking around in quake though isn it? Yeigh!
 

basementApe

Member
Hey man I'm sold!

As someone who's been lost down the Q1 modding/mapping rabbit hole for the last few months it tickles me to see you've got the e1m1 map up and running in GMS2/Warp3D with lightmaps and everything. Very very cool! I love using Trenchbroom (the map editor of choice for most q1 mappers) so this has me intrigued.

I haven't followed the progress, just became aware of Warp3D. What model formats outside of obj are supported? I assume fbx or collada or something? Would you consider adding md3 format support if it's not too much of a pain? Md3 is the vertex animation-based model format used by Quake 3. It's functionally the same as Quake 1's mdl format except without any (visible) vertex distortion while animating (the original mdl format can make models look like wobbling bags of jello sometimes). Something I've missed when messing around with modern 3D engines is the lack of vertex animated model formats. You can do types of animations with those, like morphing into different shapes, that are either hard or impossible to do with bone-animated models.

Anyways, look forward to more updates on this!
 

Joe Ellis

Member
Thanks for the support!
Currently only obj and d3d models can be imported, but I'm going to work on more formats when the beta version has been released.
I'm working on importing BBMOD files, so then everything that that and assimp is compatible with can be imported.
And also @Bart is working on a blender exporter to export blend files to w3d.

I really want to get vertex animation set up aswell.
I've got it set up several times in the past, but I think cus before when the skeletal animation system wasn't set in stone, it was too confusing trying to get vertex animation set up aswell. But now I think I can kind of just hack the animation editor to work with vertex animations aswell (if there aren't any bones in the model).

I'll start working on that as soon as the beta version is released, cus I need to prioritize getting everything ready for that at the moment.
I had a look at the md3 format and I'm going to try to get something working, probably by converting some source code for an importer online. :)
 

Garström

Member
I'am curious, what happend to the old animation system?

What features are going into the beta?

How easy is it to use my own code into warp?
Did you use a modular approach to the programming?

Also I think that supporting bbmod is a good idea.
 

Joe Ellis

Member
BIGGEST UPDATE SO FAR:

I have just released the beta version on itch.io! (Yieah mother ****ers!)

https://joemotion.itch.io/warp3d

I've also updated the OP so if you bookmark it, you can easily download the latest version of the manual, which I'll be updating every few days whenever I write anything new.

Now that I've released the beta version, and got a huge ton of itelly bittelly things out the way, I can focus on BIG improvements, and stuff that I had to leave while getting the beta version ready. So I'm really excited about what I'm gonna do next!

This is a rough roadmap:

  • vertex animation (plus importing from md3 or similar format that uses vertex animation)
  • lightmapping (The majority of this is working already, but I need to make the options window for it to control everything)
  • shadowmapping
  • render to texture - mirrors, water reflection\refraction, portals, cctv cameras that show up on screen
  • Dynamic precise colmesh collision (This already works, it was used by default but I had to take it out cus it was too slow doing it for everything, so there'll be a new mask type "colmesh dynamic" which does this) So it can be used for things like big moving fan blades that you can stand on and the character rotates with it. Or any moving\rotating platform or object with a colmesh. (So currently colmesh entities can't move)
  • Add model import for BBMOD, SMF, D3D, and .blend files
  • "Events" - A system I made for making custom things happen in levels. (You can currently create events and add actions to them, but I haven't tested any of it yet, so this will be tested and finalized)
  • Improve the "zone" system. You can currently create them but I commented out the "view_zone_step()" in the engine cus there were a few problems. But this will be able to be used for major culling and deactivating entities in far away areas.
  • Functions in the model editor will have an options window: auto_rig, add_noise, smooth, and various other functions that will work better with having some input parameters.
  • *possibly* an object based rendering system. One object & instance for each shader, then all the relevant vformat and rendering can be contained in each object. Plus it would give the ability to choose between the 3 draw events (begin, main, end) which will be better with transparency, render to texture, and also optimize the rendering process cus it would no longer have to iterate through the list of active shaders. (Probably very minor tbh, but I think it could be a lot more easy to work with)
  • There will be several improvements to the example objects, mainly minor tweaks to fix glitchyness.
  • Also I'll add swimming animations to eggzample and add more functionality for objects dealing with water.
  • I'll also be overhauling the colmesh system to work with triangles as arrays with macro accessors instead of structs with variables, cus after performance testing it seems about 1.5 times faster.

So that's it for now, I should sleep well tonight haha, I've definitely hit a big milestone with Warp3D :D

If any of you buy it and have any questions, feel free to ask here, or on itch.io. I'll try to help the best I can. And I know the manual doesn't have anywhere near enough in it yet, so I'm gonna be adding stuff to that all the time.
Also any suggestions, feedback or feature requests are welcome, and it'll really help me improve it and make it as good as it can be!
 

Joe Ellis

Member
@Garström sorry for the late reply, had to get that big announcement out first haha

The old\vertex animation system was made a few times, but because I had to focus on the skeletal animation more (cus it's a more expected thing that an engine needs) I had to put the vertex animation on hold, and I never ended up completing making it work with the animation editor and all the rendering setup. But I am going to focus on it more now.

In terms of coding with Warp3D, I've designed all the functions to be as familiar as possible (similar name formatting and argument types)
There are a few things you need to do differently, for instance every object has to have a user event 0 and in it defines the variables for that object (which get passed on to all entities\instances of that object) This is so the engine can keep track of the variables and save\load them. And also so the editor can edit them, and know what variable type it is and do the right thing when you click on it.
But apart from that, with the way it works, you get to code objects pretty much the same as normal. They can still use collision events, and inside them you just add one of the collision functions that the engine has, like collision_capsule_solid. That's what the characters use when colliding with any solid object and it automatically does all the collision stuff based on the solid object's mask (cuboid, cylinder, sphere, colmesh\precise)
So I hope with the way you can just call the different functions you need for the 3d engine stuff, you're left with being able to still be creative and experiment with gml..

Modular approach: Yes I think it's pretty modular, cus all the functions in a catagory are grouped into scripts, so everything function to do with a certain thing can be found in the same script, and start with the same name (entity_, group_, material_), and the way the manual is laid out is very similar to how the project is.
The project is also split into 2 parts: The Warp3D Engine folder, and the game project. All the game project stuff isn't essential for the engine to be able to run, but it contains all the game objects and certain code for the game. These parts are designed to be edited by the user, whereas everything in the Warp3D folder shouldn't be, unless you really know what you're doing. So I've split them like this so that people can feel free to edit the game objects while keeping all the engine stuff out of the way.

Also yes with BBMOD import it will be compatible with everything (using bbmod gui as a bridge), the only thing missing is vertex animation formats, which are all old now. So I need to use md3 or something that's the best quality vertex animation format.
 

Garström

Member
Got an error upon compiling,
___________________________________________
############################################################################################
ERROR in
action number 1
of Create Event
for object obj_engine:

File is not opened for reading.
at gml_Script_init_project_folders (line 1451) - line = file_text_readln(f),
############################################################################################
gml_Script_init_project_folders (line 1451)
gml_Script_init_files (line 1141) - init_project_folders()
gml_Script_init_engine (line 107) - {init_files()}
gml_Object_obj_engine_Create_0 (line 8) - init_engine()
gml_Room_room_main_Create (line 2)
 
Last edited:

Joe Ellis

Member
@Garström Hi sorry for the late reply, I always go to see my family on sunday and I haven't been on the computer.

I think the problem will be cus global.game_directory isn't where the project is, so it can't open it.
To set the game directory you need to edit the first part of scr_init:

GML:
function init_engine()
{

    //Game Directory Setup
    global.game_name = "Warp3D Beta"
    if global.exe_mode
    {global.game_directory = program_directory + "/"}
    else
    {
    global.game_directory = "C:/Users/" + string(environment_get_variable("USERNAME"))
    + "/Documents/GameMakerStudio2/" + global.game_name + "/"
 
    ///If your gm project directory is different to this - comment out the above 2 lines
    ///and write it manually below:
 
    //eg. global.game_directory = "C:/GM Projects/" + global.game_name + "/"
 
    }
You might need to write out the directory manually, eg. global.game_directory = "C:/GM Projects/Warp3D Beta/"
Also if your hard drive isn't C it'll have to be changed.
Let me know if that doesn't work and I'll see what else could be causing it :)

I love that meme btw hahaha

EDIT- I just realized you're the guy who bought it in 2018. Sorry I thought you were someone else, did you change your dp or name?
Anyway I can give you a full refund if you want? I can't remember how much it was in 2018, but you still bought in then so you should get the new version for free.
 
Last edited:

Joe Ellis

Member
-Quick update-
I've just changed the price to $14.99 as I think it's more reasonable for the beta version. If you've bought it and want me to refund you the $5 just PM me and we can sort it out.
I should be uploading a new version in a couple of days with a few bug fixes. And a free exe demo that'll give you full access to the editors and example game :)
 

Garström

Member
No, I don't need a refund. I spent 20 then and 25 now. I think that is a fair price. I mean this package is massive and 15 is selling yourself short. Just don't make me buy a third time.

Also another error
___________________________________________
############################################################################################
ERROR in
action number 1
of Draw Event
for object obj_async:

global variable name 'default_palette' index (100989) not set before reading it.
at gml_Script_palette_load (line 36) - {return global.default_palette}
############################################################################################
gml_Script_palette_load (line 36)
gml_Script_init_defaults (line 1192) - global.default_palette = palette_load("Default")
gml_Script_init_functions_async (line 1681) - init_defaults()
gml_Object_obj_async_Draw_77 (line 6) - {task()}
 
Last edited:
Top