Medieval themed 2D strategy game

Hello! I’ll be using this thread to document my progress while building a mobile strategy game. This is my first game and I’m newish to programming. I realize the importance of starting with a simple snake or pong game, but I’d rather gradually build the game I want while learning along the way. This will end up being the more-challenging, yet more-rewarding, path.

I made a video about why I’m creating my own game. Basically, the reasoning comes down to multiple factors. The first is that I’ve always loved games. The second is what I’ve been doing primarily for the past several years is advertising and marketing. I started as a copywriter, which I consider to be an emotionally-driven, sales-based job. You can say I’ve had success.

For the past 4-5 years, I’ve been traveling the world non-stop while living and working from hotels. To date, I’ve visited 35-40 countries. At the time of this post, I just wrapped up 2 months in Dubai; and, I’m currently in Istanbul… weeks away from getting married!

So, back to gaming. While working in advertising, I’ve also been involved with buying media and tracking campaigns. Doing this is very satisfying – I love analyzing the data and seeing how to improve my results. For all of these reasons, I’ve decided to combine my talents with my interests and create the game I want.

I started with Unity, but found it to be too overwhelming for a beginner. One of the programmers I hired (before I decided to program the game myself) recommended Defold. I’ve been a Defold user for weeks and while it can be challenging, I’m already getting satisfying results.

Here’s an example of what I’ve created so far — a sandbox for my game:

Features I’ve implemented so far:

  • Custom render for background
  • Orthographic camera
  • Click to move camera
  • Mouse wheel to zoom in/out
  • Smooth animations

These are all basic features, but they took me some time to learn. I wrote my code based on answers in these forums and the examples/tutorials from Defold. I realize there are assets I could have used, but I wanted to understand why and how these features worked. I feel like this will be important down the road.

About the game: it will be a strategy-based game with a medieval theme. There will be armies to fight with and merchants to collect resources from other towns on the map. I have A LOT of ideas to implement, but I’m taking things one step at a time.

Now that I have the basic camera and controls set up, I think the next step is to think about the hex tiles or the buildings/units. Lots of directions to go in!

18 Likes

I’ve hit a bit of a roadblock recently. While I’ve set up the cursor, zoom, and movement, the screen isn’t following the world cursor.

I believe the problem is because the mouse to world coords are incorrect, as seen in the Defold help documents. The problem is I’m not sure where to put this code or how it works.

I imagine it will take me at least a week or so to figure out what’s going on here.

I’ve tried the Orthographic and Cursor assets, but those were not easy to figure out and not as “plug and play” as suggested. They also have a lot of things I don’t fully understand yet going on in the code, such as lua modules. I figure if I will take the time to understand and set up an asset, I might as well use that time to create and implement my own solution.

This is frustrating because it’s delaying the fun part of building the game, but it’s a necessary part of the process. Another option is to hire someone to set up the camera how I want and pick back up with creating the actual game.

2 Likes

Still trying to figure this out. I’ve been told to look here for help, but I’m still lost.

I’m now thinking Defold might not be the engine to pursue for the type of game I want to build. Come to think of it, I haven’t seen the type of map and interface I have in mind within any of the examples. I’m not saying it’s impossible, but perhaps I’m taking too big of a leap with this.

Another alternative idea is to set up my game entirely with GUI elements. Maybe that would allow me to always be able to click where I want to on the screen. The type of game I’m motivated to build is called Hades’ Star. It’s a simple 2D strategy game on the surface, but has a lot of depth that I like.

You can see some of the gameplay here https://www.youtube.com/watch?v=o9JJiBqFxxE

As for my progress, here’s where I’m at to date:

kingdoms and armies.zip (1.6 MB)

2 Likes

While I applaud your willingness to learn everything from scratch I believe it might not always be the best option since there is so much to learn in the beginning. I would much rather use an extension to handle the camera and input as those take care of the hard parts so that you can focus on your game.

I will spend some time this evening creating a starter pack for a use of a camera and input extension and use it in the context of a resource management/map based game such as yours.

6 Likes

I’ve created a template project that can be used to get started with a camera (Defold-Orthographic) and mouse input (Defold-Input). You can find the template project here:

5 Likes

You are right. When I first tried those assets, they were not easy for me to set up and I figured I’d figure out a solution without them. In retrospect, it would have been easier to learn how the assets work.

Thank you very, very much! Trying it out now.

Now that the camera and cursor is working as intended, I’m figuring out how to control the unit. The plan is to:

  1. Click the unit
  2. Open a panel
  3. Select the move option
  4. Choose a destination

Units will be restricted to moving to destinations. They can move to one destination and, if they choose, queue up waypoints.

I’m keeping things simple for now and figuring out movement first. I’ll then look up how to queue up additional commands. Maybe a table or coroutine would work best.

Here’s the code that’s making it happen. Open to input, ideas, or suggestions!

go.property("speed", 200)
go.property("moving", false)
go.property("destination", vmath.vector3())

function set_destination(pos)
	pos = vmath.vector3()
	return pos
end

function move(self)
	print("move")
	self.moving = true
	self.destination = vmath.vector3(350, -64, 0)
	self.pos = go.get_position()
	local distance = vmath.length(self.destination - self.pos)
	go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, self.destination, go.EASING_LINEAR, distance / self.speed, .25, waypoint)
end

function waypoint(self)
	print("waypoint")
	self.destination = vmath.vector3(-350, -64, 0)
	self.pos = go.get_position()
	local distance = vmath.length(self.destination - self.pos)
	go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, self.destination, go.EASING_LINEAR, distance / self.speed, .25, final_destination)
end

function final_destination(self)
	print("final")
	self.destination = vmath.vector3(0, -64, 0)
	self.pos = go.get_position()
	local distance = vmath.length(self.destination - self.pos)
	go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, self.destination, go.EASING_LINEAR, distance / self.speed, .25)
	self.moving = false
end

function init(self)
end

function update(self, dt)
end

function on_message(self, message_id, message, sender)
	if message_id == hash("move") then
		if not self.moving then
			move(self)
		end
	end
end
3 Likes

Stacking waypoints in a table is definitely a good way to do it. I’d create a function (e.g. process_next_waypoint()) that you could put as the complete_function in your go.animate() animation.

In process_next_waypoint(), you would instruct the object to move to the first waypoint in your table, then get rid of that value. The animation of course calls process_next_waypoint() again, so it continues until you are out of waypoints. If the table is empty when process_next_waypoint() is called, you return out of the function instead of creating an animation.

By the way, your functions are all global which might produce unintended behaviours. I recommend making them local unless you’re doing it on purpose!

1 Like

Sounds like a good way to do it. Need to spend more time and figure out how everything works.

Definitely not doing it on purpose! Thanks for that.

When I set them up as local, they’re not responding to the call at the end of the animation.

To be clear, the lifecycle functions (init(), update(), etc) should remain as they are. The reason the others should be local is if you later on define a function of the same name in another script, it will replace the function in this script.

1 Like

Made a small example. This works for me:

local function splice(t,i,len)
    -- t = table
    -- i = location in table
    -- len = number of elements to remove
	len = len or 1
    if (len > 0) then
        for r=0, len do
            if(r < len) then
                table.remove(t,i + r)
            end
        end
    end
    local count = 1
    local tempT = {}
    for i=1, #t do
        if t[i] then
            tempT[count] = t[i]
            count = count + 1
        end
    end
    t = tempT
end

local function process_next_waypoint(self)
	
	if #self.waypoints == 0 then
		return
	end

	local distance = vmath.length(self.waypoints[1] - go.get_position())
	local t = distance / self.speed

	go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, self.waypoints[1], go.EASING_LINEAR, t, 0, process_next_waypoint)

	splice(self.waypoints,1)

end

function init(self)

	self.speed = 100

	self.waypoints = {
		vmath.vector3(200,100,0),
		vmath.vector3(100,200,0),
		vmath.vector3(200,100,0)
	}

	process_next_waypoint(self)
end
2 Likes

Interesting! More code to decipher. Thank you!

Going back a bit, why wouldn’t the previous code I had work when I set my functions as local?

Not sure. It could be because the order in which you declare functions matters. If function B calls function A then function A must be declared above function B. Perhaps that isn’t the case for globals?

In your script, one example of this is move calling waypoint but waypoint being declared under move.

1 Like

Makes sense. It must’ve had something to do with the order in which I was declaring. No matter, it was just test code to see how to connect animations. The waypoint system is the one I want to use. Now, I need to figure out how to create a GUI that allows the player to move and add/remove waypoints.

More on variable scopes in Lua here: https://defold.com/manuals/lua/#locals-globals-and-lexical-scoping

2 Likes

Thank you. I’ve read this a couple times, but still haven’t comprehended it completely.

I’ve now decided to take time to reviewing and studying Lua before implementing the next aspect of my game — a GUI that allows basic commands, such as moving the player and adding/removing waypoints.

Good choice! It really helps to be comfortable with the Lua language. Let us know if you have any questions.

2 Likes

Today I’m working on figuring out how to implement the GUI. I understand how to enable/disable the GUI from cursor inputs by passing a message to the gui script.

What I don’t understand is how to set up interactions between game objects and GUI elements. When I click the GUI, I get “nil”.

I’ve taken a closer look at assets and lua modules that connect game objects and GUI elements. I’ll likely implement one of those to get this working.

GUI interaction is different and handled inside it’s associated .gui_script file.

Super simple example of a GUI with a button: