John Space - a 3D Top-Down Action game

Hello!

I started to show my work in this thread as I wanted some feedback on the character design; but I thought I should make a thread for the actual project that is work-in-progress.

Main Inpirations

This project is based on one of my comics character John Space:

The gameplay I am aiming for will be inspired by:

  • Lara Croft and the Guardian of Light: Tight cooperation between two players, in a top-down twin stick shooter and puzzle game
  • Zelda series: small open-closed world organised around dungeons and a NPC village. Dungeons focus on one gameplay mechanic.
  • Ratchet and Clank: Fun and Groovy sci-fi style

The co-op will be local only, and may be optional. I wanted to make the game only playable at 2 players, but I am still unsure.

Main Character

Here is the design of the main character John Space (from the thread I linked above):

He will be joined by a second main character, most probably Jane Space, when I start working on the co-op :slight_smile:

Story

John and Jane Space are space rangers of the Intergalatic Trust. They are sent on planet GX-348 to investigate the traces of an extinct civilisation of intelligence class C (i.e. equivalent to human intelligence) and the inexplicable hostility of the indeginous creatures against Class C and B species.

Style Exploration

I started a style exploration in February. Here where I started with Nomad Sculpt.

After a lot of work on creating textures in Substance Designer, writing shaders, adding flora and some animations, I got a little closer to what could be the “final” look/style of the game:

It is been quite challenging to get there, and I wish I had maintain a series of post on my weekly progress! So I will try to give weekly update from now on.

23 Likes

Hey, I remember you!

I’m glad to see your project is progressing. (Although, I thought “John Space” was a temp name, because of the real-life “John Doe” used as a placeholder on legal documents)

Taking from my own devlog experience, please remember to pace yourself with these devlogs. Game development is both easier and harder than it looks. And the amount of progress you’ll make in a given devlog period isn’t consistent period-to-period.

Also, make devlogs the accommodate your lifestyle, your workflow, and your mental wellbeing; rather than public perception. Make devlogs in a format your comfortable with, and if something doesn’t work for you drop it, communicate why, then change things up.

Also also, where else are you posting your devlogs? I suggest you diversify via your own blog and/or social platforms. Because the Godot forum isn’t the most optimal place to do frequent devlogs that attract new supporters from my experience.

I’m not saying you shouldn’t post devlogs here. What I’m saying is that this isn’t a long-term solution. Your initial post will get some traction and support (me), then your post will be pushed down the list by help posts (This is actually a good thing). The same thing will occur when you post your next devlog, the one after that, and the one after that, etc.

You should also talk to people personally and make some friends. Either online and/or real-life. You don’t even talk about your project, but about anything really. Because your biggest supporters are the ones who are outside your project.

I use discord for this. You ought to join the official Godot discord and the Godot Café. They have voice chats daily with other developers and artists like you and I.

And hey, I’ll extend my metaphorical olive branch: You can feel free to message me on Discord (Username: demetriusdixon). We can chat about anything you like and I can introduce you to more people I know on the servers I mentioned.

Some more tips:

  • AVOID REDDIT AT ALL COSTS!!! It’s snark central and you won’t get sincere feedback or support sustainably. And since it’s a (sub)forum, your posts will be pushed down with time. (I hate it, if you couldn’t tell. You ask something sincere, then you are rewarded with a lovingly gift-wrapped turd)
  • Please Prioritize Sustainability (or PPS for short). Slow, but steady progress is always better than big, infrequent bursts. You’ll need energy to work tomorrow, the next day, and the day after that; for an unknown amount of time.
  • Appreciate people like me. I know that sounds cocky. What I really mean is: you should really appreciate people who’ll go out of their way to tell you something or help you out (even if they’re wrong, imperfect, or opinionated). You never know when it’s gone (This is why socializing is mandatory).

I wish you the best of luck with John Space. Pace yourself. Stay sane. I mean it.

3 Likes

Thank you very much for your interest!

yeah, I never know what to publish on Reddit to be honest. I only ever used it for marketing. I mostly post on Twitter and Bluesky at the moment, but I am looking for a place where I can get more of a community vibe, get more feedback and longer messages like yours! I miss the 90s, 00s internet vibe :sweat_smile: I should give Discord another try, actually :slight_smile:

Making friends and be sociable is indeed the hardest part of being a solo game dev. It is indeed super important on the long term to prioritise social life to stay sane and give some perspective to our work and passions. This being said, I am planning to go to gamedev meetups once I have something to show!

In terms of pace, I’ve learn the hard way the cost of being too involved in a project when I released my first game on Steam. So I am doing my best to keep things simple and fun this time and limit the pressure at the moment. The problem is that as soon as you aim for commercial release/success, you will have to pressure yourself, because competition is tough.

I send you a friend request on Discord soon, thank you!

2 Likes

I added a running animation and some lerp transitions between the idling and running states and between the changes in direction.

That’s so exciting to be able to move in John Space’s world now!

I also recorded a comparison, with and without the lerp transition (look at when John Space starts and stops running):

I will be posting my work on the jump animation soon!

3 Likes

This looks phenomenal! It’s just as expressive as the concept art! That’s surprisingly hard to achieve.

Edit: This statement is a little off-the-cuff, but if I had the money, I would definitely hire you to make concept art and/or 3D models. But I don’t have the money, so that’s staying a fantasy.

1 Like

Thank you! I am doing my best at transposing what I have in my mind into 3D.

1 Like

Good morning!

I added a little jump animation last week to get a better feel of how the game would play. This week I will be working on more animations!

I also started to work on new enemies:

The idea is that the big crab on the left (it will be bigger) will be throwing sticky balls over the map, which will slow down or stuck the player, before to attack. The smaller crab on the right is more of a swarming enemy (low life, low meelee attack, but many). When both are on the map, the player will be in great trouble as their actions are complementary.

Also I started to look at the color scheme I could be using to differentiate which enemies is attackable by Player 1 or Player 2.

The idea is that the game will have a coop mode. Player 1 can only affect the enemies with purple accent, while Player 2 can only affect the enemies with yellow accent.

7 Likes

:partying_face:Back from a road trip to Scotland :sunrise_over_mountains:, back to gamedev :joystick:! :partying_face:

I started to work on a 3D platform generator before my vacations:

And now I am adding collision shapes, and testing the platform with the main character.
I probably need to simplify the collision mesh even more though. My computer is having a hard time running this demo!

Note that I also changed my approach (using 1 surface for the top platform and 1 surface for the side wall), so that the transition between the top and the side is not so abrupt as in the first video. I know have all the vertices in one surface and use the SurfaceTool.generate_normals() on it, which results in a smooth edge between the top and side wall.

8 Likes

Hey!! Great to see your game! It’s truly awesome looking!! I love the lerp from one animation to another. Is this possible in 2D sprite animations??

Great work, can’t wait to see more!

Thank you!

No, unfortunately, in 2D sprite animation, it would not work, the lerp is interpolation the bone rotations. It could work with 2D skeletal animation though (e.g. using Spine from Esoteric Software).

2 Likes

aww yeah, that makes a ton of sense. Dude, please let me play test this when it’s time!! Keep up the awesome work!

1 Like

The high to low poly workflow is hard, but I finally baked my normals correctly, so here is a preview of the new little crabs!

I was having some troubles I could not comprehend with the normal maps, as if the back legs had their UV flipped. But also noticed that the front leg did not look good either (it was upside down):

I tried flipping the normals, transform the normals, flip the UVs (S then -1, then rotate 180), nothing worked.

Turns out that I was baking multiple parts of my mesh into the same uv space (to save UV space) and the baker (i.e. I use Subtance Painter to bake) did not like that. Here you can see that all the legs are projecting to the same UVs in orange:

When I finally realised what was going on, I removed all mirrored and linked duplicates, and only baked what it is needed (although I forgot to deleted the right claw, but well the baker did not complain):

So if you read this and are struggling with a bake, remember to check that you deleted all mirrored or linked duplicate before baking!

9 Likes

This is looking great. Love the crabs!

2 Likes

Hi, I am working on a personal project for a golf like game and I just ran into your post. First of all, the amount of work and small details you have put into this are not unnoticed, absolutely stunning work!

I have never thought about doing terrain generation with Path3D nodes before reading through your post. So now I am rethinking my entire life :sweat_smile:, but hey that is the exciting part about learning new things.

Could you give us some additional insight on how you achieve generating the top or inner surface of your Path3D?

I tried it out with a small script using SurfaceTool to generate the top layer using the Path3D’s curve points: curve.get_baked_points() and that works pretty well actually as long as the Path3D and PackedVector2Array are setup properly. So thank you for that context.

I am at a loss how you were able to see the top layer in your 3D editor though, so I think I might be completely misunderstanding how you achieved your result and there is a much better solution for all this instead of doing mesh generation with SurfaceTool :smiley:?

Any help would be much appreciated, keep up the great work!

2 Likes

Thank you for your interest in my work :slight_smile: !

I will be probably publishing the code as a plug-in at some point, but here is the function that update the cap/top part of the mesh. Note that I am using only one surface for the top and the side walls now.

func update_top_surface(arrays:Array, for_collision=false):
	# Here, I use curve.tessalate to extract points from the curve:
    if not for_collision:
		top_vertices = self.curve.tessellate(3,4)
	else:
		top_vertices = self.curve.tessellate(1, 4)
		
	arrays[Mesh.ARRAY_VERTEX] = top_vertices
	
    # Here, I convert the results to a 2D curve to access the triangulate function
	v2d_array = PackedVector2Array()
	for v3d in top_vertices:
		v2d_array.append(Vector2(v3d.x, v3d.z))
	top_triangles = Geometry2D.triangulate_delaunay(v2d_array)
	top_triangles.reverse() # flip normal to be facing up
	delete_outside_faces() # in this function I use cross-product to filter out outside faces
	arrays[Mesh.ARRAY_INDEX] = top_triangles
	# Here, I create a temporary surface access the deindex() function
	# because the side wall is using a deindex format
	var st = SurfaceTool.new()
	st.begin(Mesh.PRIMITIVE_TRIANGLES)
	st.create_from_arrays(arrays)
	st.deindex()
	arrays[Mesh.ARRAY_VERTEX] = st.commit_to_arrays()[Mesh.ARRAY_VERTEX]
	arrays[Mesh.ARRAY_INDEX] = []
	# NOTE: we keep the top_vertices in indexed format, as we need it in this format 
	# to start the extrusion

You can see it is a bit convoluted because I try to use build-in functions such as Geometry2D.triangulate_delaunay() and deindex(), which are not available without little walkarounds.

2 Likes

Some progress on the animations of the little crab after an afternoon rigging and weight painting :smile:

little-crab-walking

8 Likes

Hey thanks for sharing that is awesome, I was doing something like this, which I think is similar? Not sure if my approach is less performant or not but it worked :smiley: :

	var points = curve.get_baked_points()
	var offset_points := []

	for i in range(points.size()):
		var p = points[i]
		var offset_y = p.y
		offset_points.append(Vector3(p.x, offset_y, p.z))

	var center = Vector3.ZERO
	for p in offset_points:
		center += p
	center /= offset_points.size()

	var surface_tool = SurfaceTool.new()
	surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)

	for i in range(offset_points.size()):
		var a = offset_points[i]
		var b = offset_points[(i + 1) % offset_points.size()]
		surface_tool.add_vertex(center)
		surface_tool.add_vertex(a)
		surface_tool.add_vertex(b)

	surface_tool.generate_normals()
	surface_tool.generate_tangents()

Playing with the curve.bake_interval prior to baking really helped the quality of the vertical deformations, I settled on 0.5 anything less than that seemed to cause noticeable enough drops in performance.

After building that I am just setting putting it into the mesh and setting the MeshInstance3D’s cull mode to CULL_DISABLED to flip the surface texture and setting whatever custom material through set_surface_override_material.

2 Likes

I was wondering what is the difference between get_baked_point() and tessellate() and I found some info here: `Curve2D.get_closest_offset()` returns wrong value · Issue #62301 · godotengine/godot · GitHub

It seems that:

Internally, get_baked_points calls tessellate_even_length. The main difference is the former is cached and the latter is calculated on the fly. On the other hand, tessellate use a different adaptive algorithm, which produces very uneven points(dense near corners, sparse in straight segments).

In other words, tessellate() adds geometry where there is a need for it.

In my version I don’t compute a center because if the shape is concave, the center could be out of the shape. However, I am very interested in a way to add more evenly distributed triangles in my top surface. One way I thought about was to use a grid of points (and filter out the ones that are outside the curve).

Finally, if you are adding triangles around a center, you can use the SurfaceTool function add_triangle_fan() I think, where the first point is the center point, followed by the points around it: SurfaceTool — Godot Engine (stable) documentation in English. This way you can avoid a for-loop with gdscript.

2 Likes

That is an insanely good callout I didn’t even think of! Most of my shapes weren’t that extreme in my use case, so I didn’t even think of the deformation issues that calculating the center could cause. So I put it to the test and you are totally correct:

You can see because of the way I was doing it in there is an odd slope if I push the shapes too far. Looks like I am going to have to go back to the drawing board and update my implementation to use tessellate.

Thanks for geeking out about this with me :smiley: .

4 Likes

Have to split this out because I can only post one image being a newbie, per post. Here is the updated version using tessellate:

I can tell its trying much more to conform to the shape of the curve. Thanks for calling that out :slight_smile: .

2 Likes