Monthly Archives: May 2016

Games As A Service

Today we are going to talk about Evergreen games. Some games get away with being both Evergreen and Consumable like Call of Duty and Halo with their consumable single player campaigns and evergreen multiplayer service. I got some of this idea from reading an article written by Daniel Cook about playing a single game as a lifelong hobby. He put into words several ideas that had been bouncing around in my head for a few months about the kind of game I would eventually like to design.

I am a (Fill in with Game Name) Player

Many non-digital games are often played many times over the course of ones life. And often people get associated as a player of that game. He is a chess player. She is a soccer player. He is a basketball player. She is a poker player. And the list could go on.

The idea is to create a game that people enjoy playing over and over and make playing it part of their identity. This is where a lot of the E-sports games come from.

Game as a Service

There are a quite a few examples of games as a service out there, just pick almost any E-sport game and you have one. Almost all of them share a common set of attributes.

  • There is a low barrier to entry. Usually free or a small one time purchase.
  • The game is easy to play but hard to master.
  • The community is stable and there are competitive events around the game.
  • There is some sort of cycle of new content or regular changes that keep long time players interested.

A lot of these attributes are focused on the Strategy and Hooks section of the 10 things every game needs. You need the hooks like a low barrier and a large community to get players interested. Then the slow influx of new content and the deep strategy or at least slightly evolving strategy based on the changes keep the players interested.

The Connection

With my current game, I want to design an experience that people can play in an evergreen way. So I need to study what kinds of things make for a good evergreen play experience. In addition to writing the article that inspired this post, Daniel over at Lost Garden games also worked on Steam Birds and wrote an article that talks about how they designed it to be more evergreen.

I plan on using these insights to better design my as yet unnamed game to be able to be played over and over again.

April 2016 End Of Month Goal Review

Time to review how we did on our goals in April. Spoiler alert, it’s not pretty.

Goals

  1. Write 3 Blog Posts Per Week and Publish 2 – Completely fell on my face on this one for the month of April. Part of this is just not making it a priority and part of it is I spent 4 weekends each doing a 14+ hour drive. A couple of those hours are the ones I usually spend doing my writing. This is not something I will be doing the rest of the year and is something I was hoping to cover by having a buffer of posts built up. So the real let down is the lack of buffer to cover for times like this.
  2. 1 Game Designed, Created, and Released Per Quarter – Much more fun to talk about this particular area. I did get the first game of 2016 deployed to a server on Digital Ocean, but did not keep it out there as the game itself is not really fun and needs more work. I also began work on game #2 and have been having fun working on movement basics and thinking of ideas. More on this later.
  3. 1 Book Read Every 2 Months on Game Design – Not much reading has been going on recently. I did get a few more chapters through the Art of Game Design and am will finish it this month. Only 70 pages left at the time of this writing.
  4. 1 Article Read Or 1 Video Watched About Game Design/Creation Per Week – I spent some time on Lost Garden reading some blog posts but have not taken any notes. I may discuss some of my thoughts on one of the post regarding multiplayer since I hope to make successful multiplayer games in the not too distant future.
  5. Get 100 People Reading Evolving Developer Per Month – Well I did not tell anyone about Evolving Developer in April, but I plan on telling at least 20 people at OSCON in a couple weeks (maybe more than 20, who knows). Not the best audience but last year I attended a couple sessions on games and game design so I know there are at least a few like minded people attending.

What Went Right

I made a successful deployment to Digital Ocean of game #1. This went much smoother than I expected thanks largely to an awesome tool for doing MeteorJS deploys. Game #2 has started with development already going at a much faster pace than game #1. This is partly due to a higher level of excitement about the overall game design and partly the decision to switch back to Monkey-X which is designed to be used to create games and surprisingly easy to be productive in.

What Is Not Perfect Yet

Too much travel which could not be avoided. So really, no buffer of writing built up for times when I can’t write because of other important life things happen. This was mostly avoidable and could be prevented by a little more effort. Additionally, still no progress after 4 months of getting a readership for this blog. Frankly it is scary to even write these things in a public space where people might stumble across it, let alone point them to it.

Corrective Measures

Better planning. I realized when I had a long list of blog topics I wrote more consistently. The list has shrunk since it only covered W1 (from January to the end of March). I need to make a new list for Q2. Also I need to follow the advice I read and start showing of my writing and games as publicly as possible and have a little tough skin to take any criticism constructively.

More Fail, But I Won’t Stop

Moving Along A 2D Path

For game #2 of 2016, I wanted to use a movement system similar to one that can be found in Steam Birds, Critical Mass, and some other games. I am most of the way there and would like to talk about some of the different approaches and barriers I have run into so far.

Hermite Interpolation

Gonna start with the difficult sounding silly math that is not really what I needed and also not as complicated as it sounds. Especially since the code for it is out there and you can almost copy pasta it into whatever language you are working in.

In Monkey-X it looks something like this:


Function CubicHermite:FloatDeque (start_point:Float, end_point:Float, start_velocity:Float, end_velocity:Float)
	Local pathPoints:FloatDeque = New FloatDeque
	Local division:Float = 30.0
	
	For Local i:Int = 0 Until division
		Local t:Float = (Float(i)/division)
		Local t_square:Float = t * t
		Local t_cube:Float = t_square * t
		
		Local a:Float = 2*t_cube - 3*t_square + 1
		Local b:Float = -2*t_cube + 3*t_square
		Local c:Float = t_cube - 2*t_square + t
		Local d:Float = t_cube - t_square 
		
		Local point:Float = a * start_point + b * end_point + c * start_velocity + d * end_velocity
		pathPoints.PushLast(point)
		
	End
	Return pathPoints
End

Basically you use this method to find some number of points along the curvy path that you are trying to create. You need to know what direction and speed you are going to begin, the direction and speed you will be going when you are done, and what point in time you want to get on the line. Essentially if you wanted to move along the line at 30 frames per second you will need 30 points to render whatever you are moving at. You would loop through the function doing your interpolation 30 times where the point in time is 1/30 to 30/30. You push all of these points into some sort of ordered data structure, in my case a Deque.

Note: This particular implementation only finds 1 part of the coordinate. If you are in 2D you need to run it once for X and once for Y, and if you are in 3D you need to run it one more time for the Z coordinate.

The formula then creates a nice curvy path to go from the first point to the next but ended up being not exactly what I was going for.

Simple Steps Approach

The problem I was having getting my head around with the interpolation approach was how to properly put limits on the movement. Distance was not a problem, but I only want the units to be able to turn so far.

The solution I ended up going with was to give each unit a rotational angle limit. Then I went through the 30 step loop again and on each step I calculated the angle from where I was to where I was headed. If it was greater than my current heading, we add the rotation limit to our current heading. If less than, we subtract the rotation limit from our current heading. Then we move 1/30th of our speed in the new direction and repeat.


Method SetControl(click_x:Float, click_y:Float)

	Local goal_angle = ATan2((click_y - position.y), (click_x - position.x))
	Local start_angle = heading
	Local control_pos:Vec2D = New Vec2D(position.x, position.y)
	Points = New Deque
	
	For Local i:Int = 0 Until 30
		control_pos = NewPoint(control_pos, start_angle, goal_angle, maxRotation, maxVelocity/30.0)
		If (start_angle > goal_angle)
			start_angle = start_angle - maxRotation
		Else If (start_angle < goal_angle)
			start_angle = start_angle + maxRotation
		End
		goal_angle = ATan2((click_y - control_pos.y), (click_x - control_pos.x))
		Points.PushLast(control_pos)
	End
	control.position.Set(control_pos.x, control_pos.y)
End

Function NewPoint:Vec2D (start_point:Vec2D, start_angle:Float, goal_angle:Float, max_angle_change:Float, distance:Float)

	Local new_angle:Float
	If (start_angle > goal_angle)
		new_angle = start_angle - max_angle_change
	Else If (start_angle < goal_angle)
		new_angle = start_angle + max_angle_change
	End

	Return New Vec2D(start_point.x + distance * Cosr(new_angle * (PI/180)), start_point.y + distance * Sinr(new_angle * (PI/180)))

End

Doing this lets me intuitively limit the turning radius and maximum velocity of the units. It might not be the fancy way to do it or the most efficient. But it works. And that is all we are going for.

Note about Monkey-X Documentation for ATan2(x, y): the documentation reads that it gives the Arc Tangent of x / y in degrees. Traditionally these variable names are switched. If you are thinking in typical Cartesian coordinates you will want to pass you Y value in as the first parameter and X as the second. This caused me a bit of confusion.

p.s. This code was not cleaned up for this post and a lot of it was written between 10 P.M. and 1 A.M. The purpose was not to show of how code should look but rather sample implementations of useful functions.