Jump to content
  • Advertisement
  • entries
  • comments
  • views

Update 005.1 - Controls ... Let the ReWrite Begin

Sign in to follow this  


As always, [font=arial][color=rgb(34,34,34)]the Alpha 005.1 update is up at [color=rgb(0,0,255)]http://bluestar03.com/Chronicle/Alpha[/color][/color] as well on the Windows Phone Store. The 005 Series will focus on the ReWrite I am doing since I stopped working on the game for some weeks, and when I got back, had no idea where I left. The very first thing to tackle is the controls.[/font]

[font=arial][color=rgb(34,34,34)]The game is being targeted for Windows Phone, Windows 8, Web, and Plain old Windows. Each have different capabilities for input. Overall, it is a combination of three input types: Keyboard and Mouse, Gamepad, and MultiTouch. Instead of repeating code for each input, the game figures out which inputs are supported, filters the input into an internal model, and anything that need and input, reads it from the model. There are only two versions of the game published, Browser and Mobile and each one only supports one input, Keyboard and Touch. So, when the game starts, it figures out which platform it is reading and assigns a [/color][/font][font='courier new'][color=rgb(34,34,34)]control_type[/color][/font][font=arial][color=rgb(34,34,34)] to [/color][/font][font='courier new'][color=rgb(34,34,34)]control_state[/color][/font][font=arial][color=rgb(34,34,34)]. Love that Game Maker now has enums , now, if they had private, public .... Anyways, during the Step event, if statements controlled by [/color][/font][font='courier new'][color=rgb(34,34,34)]control_state[/color][/font][font=arial][color=rgb(34,34,34)] decide how the translation to the internal model is done. But first, since I will be using this internal model, let's call it input, since all the variables are prefixed with [/color][/font][font='courier new'][color=rgb(34,34,34)]input_[/color][/font][font=arial][color=rgb(34,34,34)], I have to keep track of previous, pressed, and released states, so there are variables for that. And before anything, all the values of the current variables are assigned to the previous variables. Next, I need to get the new values.[/color][/font]

[font=arial][color=rgb(34,34,34)]First, the input model is modeled after the Gamepad. Thus, I just read the values of the Gamepad, and assign them to input. Just one small translation. The Gamepad returns the horizontal and vertical axis for each Joystick. Right now, I only care about the Left Joystick. I find using the Direction and Magnitude more useful from a Joystick. So I translate the Horizontal and Vertical to Direction and Speed using arctan2 and the Pythagorean Formula. In total, I have the variables [/color][/font][font='courier new'][color=rgb(34,34,34)]input_attack input_jump input_pause input_back input_horizontal input_vertical[/color][/font][font=arial][color=rgb(34,34,34)], as well as [/color][/font][font='courier new'][color=rgb(34,34,34)]input_direction[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_speed[/color][/font][font=arial][color=rgb(34,34,34)]. Append [/color][/font][font='courier new'][color=rgb(34,34,34)]_previous _pressed[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]_released[/color][/font][font=arial][color=rgb(34,34,34)] for the rest of the input model.

Next is the Keyboard. For the face buttons, I just need to check if the key is pressed. For the arrow keys, a bit of math. The input model is expecting axis values, not keys. Thus, for each axis pair, each key is represented by a +1 or -1, I add the value of all keys in each pair that return true, and assign that value to [/color][/font][font='courier new'][color=rgb(34,34,34)]input_horizontal[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_vertical[/color][/font][font=arial][color=rgb(34,34,34)]. This has a nice effect of zeroing the value if both keys are pressed. Now, [/color][/font][font='courier new'][color=rgb(34,34,34)]input_direction[/color][/font][font=arial][color=rgb(34,34,34)] is found the same way, using arctan2. But for [/color][/font][font='courier new'][color=rgb(34,34,34)]input_speed[/color][/font][font=arial][color=rgb(34,34,34)], as long as [/color][/font][font='courier new'][color=rgb(34,34,34)]input_horizontal[/color][/font][font=arial][color=rgb(34,34,34)] or [/color][/font][font='courier new'][color=rgb(34,34,34)]input_vertical[/color][/font][font=arial][color=rgb(34,34,34)] have a non zero value, [/color][/font][font='courier new'][color=rgb(34,34,34)]input_speed[/color][/font][font=arial][color=rgb(34,34,34)] is 1, else it is 0. One additional thing, the keys that are checked are stored in variables, prefixed by [/color][/font][font='courier new'][color=rgb(34,34,34)]key_[/color][/font][font=arial][color=rgb(34,34,34)], for example [/color][/font][font='courier new'][color=rgb(34,34,34)]key_attack[/color][/font][font=arial][color=rgb(34,34,34)] or [/color][/font][font='courier new'][color=rgb(34,34,34)]key_up[/color][/font][font=arial][color=rgb(34,34,34)]. This will allow me to later provide a way to change the keys.

Last is Touch. This one is more complicated, because all there is for me to read is a pair of numbers, the [/color][/font][font='courier new'][color=rgb(34,34,34)]x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]y[/color][/font][font=arial][color=rgb(34,34,34)] of the Touchpoint. From that I have to figure how to translate it into the input model. I first start by looping for each Touchpoint, using the temporary variable [/color][/font][font='courier new'][color=rgb(34,34,34)]i[/color][/font][font=arial][color=rgb(34,34,34)] for each Touchpoint ID. If the Touchpoint with ID [/color][/font][font='courier new'][color=rgb(34,34,34)]i[/color][/font][font=arial][color=rgb(34,34,34)] exist, I check whether it is on the left half or right half of the screen. The left side is used for the Virtual Joystick [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_[/color][/font][font=arial][color=rgb(34,34,34)], and the right side is for the Virtual Button [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_[/color][/font][font=arial][color=rgb(34,34,34)]. If the Touchpoint is on the left side, I check if [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id==-1[/color][/font][font=arial][color=rgb(34,34,34)], meaning unassigned, and if so, [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id=i[/color][/font][font=arial][color=rgb(34,34,34)]. The same with [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_id[/color][/font][font=arial][color=rgb(34,34,34)] on the right side.[/color][/font]

[font=arial][color=rgb(34,34,34)]Let's deal with the easy one first, [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_[/color][/font][font=arial][color=rgb(34,34,34)]. First, I check if [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_id[/color][/font][font=arial][color=rgb(34,34,34)] still exist, if not [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_id=-1[/color][/font][font=arial][color=rgb(34,34,34)], else [/color][/font][font='courier new'][color=rgb(34,34,34)]input_attack=true[/color][/font][font=arial][color=rgb(34,34,34)], and that is it. I do store [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_y[/color][/font][font=arial][color=rgb(34,34,34)] for future use, but that is all that works right now. The left is more difficult since it will model a Joystick. First, the same thing, if [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id[/color][/font][font=arial][color=rgb(34,34,34)] still exist or not. If it is not, [/color][/font][font='courier new'][color=rgb(34,34,34)]vb_id=-1[/color][/font][font=arial][color=rgb(34,34,34)]. Here is where it gets complicated. If [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id[/color][/font][font=arial][color=rgb(34,34,34)] exist, I check if [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_origin=true[/color][/font][font=arial][color=rgb(34,34,34)]. If it doesn't, [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_origin=true[/color][/font][font=arial][color=rgb(34,34,34)]. Additionally, [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_origin_x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_origin_y[/color][/font][font=arial][color=rgb(34,34,34)] are stored as well. This is done on the first step [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id[/color][/font][font=arial][color=rgb(34,34,34)] exist. It represents the neutral position of the joystick. If [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_origin==true[/color][/font][font=arial][color=rgb(34,34,34)], I skip this step. Next, as long as [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_id[/color][/font][font=arial][color=rgb(34,34,34)] exist, I store its [/color][/font][font='courier new'][color=rgb(34,34,34)]x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]y[/color][/font][font=arial][color=rgb(34,34,34)] position in [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vj_y[/color][/font][font=arial][color=rgb(34,34,34)]. Now I have two vectors, one for the origin, and the current Touchpoint. I then subtract the current from the origin and I get the same angle and distance the current and origin vectors have with the origin and vector (0,0). This vector is stored in the temporary variables [/color][/font][font='courier new'][color=rgb(34,34,34)]horiz[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vert[/color][/font][font=arial][color=rgb(34,34,34)]. Each one is limited to be no greater than 60 and no less than -60, then I divide it by 60 to get value between -1 and 1. I assign this value to [/color][/font][font='courier new'][color=rgb(34,34,34)]input_horizontal[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_vertical[/color][/font][font=arial][color=rgb(34,34,34)]. Now, I can find [/color][/font][font='courier new'][color=rgb(34,34,34)]input_direction[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_speed [/color][/font][font=arial][color=rgb(34,34,34)]the same way as always. At this point, the Virtual Joystick is functional, but I wanted to try something extra.

On Microsoft Build 2014, the team behind Halo: Spartan Assault gave a talk on how they made it. At this point the game wasn't released yet. One thing they pointed out is how they handled the virtual joystick differently. They described it using words like glide and adaptive. Not having played it, I didn't knew what they were talking about. Then I played it. I bought it on launch day. Technically, I got it before it launched, but anyways. The virtual joysticks were intuitive. I don't play many mobile games, so I didn't knew what did Microsoft did different. I downloaded a few and tried their controls. The difference became clear.

To describe what they did, let me describe what other games did. It is all about how they handle the origin. In Sonic CD, the origin is fixed, thus the current Touchpoint will always have the same relation to the origin at the same place on the screen. One must place the current Touchpoint in relation to the fixed origin, let's call this one "Fixed". Next is Grand Theft Auto: San Andreas. In it, there is a defined area where the origin can exist, the lower left quadrant of the screen. The first place the Touchpoint exist is assigned to the origin. So, the relationship of the current Touchpoint is to that initial press. This one is "Dynamic". The Virtual Joystick acts similar to dynamic so far, but with the entire left side instead of just the lower half of that. On both Fixed and Dynamic, the origin never changes. While in Fixed, the origin is always at the same spot. On Dynamic, once the origin is created anywhere in the defined region, it stays there until the Touchpoint no longer exist. What Microsoft did with their "Adaptive" approach is that the origin changes, the origin is being dragged. On Fixed and Dynamic, the Touchpoint can end up far enough away from the origin that it no longer changes significantly the input, and may require long travels across the screen to make a significant change. On mine so far, if the player is more 60 pixels away from the origin, it no longer increases [/color][/font][font='courier new'][color=rgb(34,34,34)]input_speed[/color][/font][font=arial][color=rgb(34,34,34)]. In Adaptive, the Origin stays near the Touchpoint, so shorter travels are needed to make changes. But how does Adaptive decide where the origin should be?

The origin needs to move in a way that keep the same angle to the current Touchpoint as before and the distance should be no more than that of the effective range, which in here is 60 pixels. [/color][/font][font='courier new'][color=rgb(34,34,34)]horiz[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vert[/color][/font][font=arial][color=rgb(34,34,34)] are a vector that is equal in angle and length as the current Touchpoint has to the origin. So the angle is right, but I need to shorten it. It needs to be no more than the effective range of 60px, but I can't just subtract that from the length. If [/color][/font][font='courier new'][color=rgb(34,34,34)]horiz[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vert[/color][/font][font=arial][color=rgb(34,34,34)] are within the effective range, less than 60px, the origin would end up on the other side of the current Touchpoint. [/color][/font][font='courier new'][color=rgb(34,34,34)]input_horizontal[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_vertical[/color][/font][font=arial][color=rgb(34,34,34)] have the same angle but is only as long as the effective range, but 60 time smaller. So I just multiply it by 60 and subtract it from [/color][/font][font='courier new'][color=rgb(34,34,34)]horiz[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]vert[/color][/font][font=arial][color=rgb(34,34,34)] and apply this value to the origin. Now the origin will stay with the same angle, and no further than the effective range. This setup feels good to me, and it will be the default. But ill allow for it to be changed in future build, just in case others like the other options better.

Now that input has the current and previous values, I can check for pressed and releases. For pressed, I check if [/color][/font][font='courier new'][color=rgb(34,34,34)]_previous==false and current==true[/color][/font][font=arial][color=rgb(34,34,34)]. Releases are the opposite, [/color][/font][font='courier new'][color=rgb(34,34,34)]previous==true and current==false[/color][/font][font=arial][color=rgb(34,34,34)]. I could make [/color][/font][font='courier new'][color=rgb(34,34,34)]held[/color][/font][font=arial][color=rgb(34,34,34)] variables if [/color][/font][font='courier new'][color=rgb(34,34,34)]previous==true and current==true[/color][/font][font=arial][color=rgb(34,34,34)]. And maybe I will. So now, [/color][/font][font='courier new'][color=rgb(34,34,34)]input_[/color][/font][font=arial][color=rgb(34,34,34)] is up to date. Time to be used.

The Player object just moves and attack right now, so that's all I'll explain right now. First, it gets a copy [/color][/font][font='courier new'][color=rgb(34,34,34)]input_horizontal[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]input_vertical[/color][/font][font=arial][color=rgb(34,34,34)] so that it may use it. These are stored in [/color][/font][font='courier new'][color=rgb(34,34,34)]hspd [/color][/font][font=arial][color=rgb(34,34,34)]and [/color][/font][font='courier new'][color=rgb(34,34,34)]vspd[/color][/font][font=arial][color=rgb(34,34,34)]. These variable will eventually be applied to the Player's [/color][/font][font='courier new'][color=rgb(34,34,34)]x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]y[/color][/font][font=arial][color=rgb(34,34,34)]. They can be applied right now, but the Player would move no more than a pixel each Step. Thus first thing first, multiply it by [/color][/font][font='courier new'][color=rgb(34,34,34)]mspd[/color][/font][font=arial][color=rgb(34,34,34)], Max Speed. Now we have the maximum potential movement the player can take, but what about walls? This is done by checking if at [/color][/font][font='courier new'][color=rgb(34,34,34)]x+hspd[/color][/font][font=arial][color=rgb(34,34,34)], the Player would collide with the object Wall. If so, [/color][/font][font='courier new'][color=rgb(34,34,34)]hspd=0[/color][/font][font=arial][color=rgb(34,34,34)]. But I still would like to move the Player as close as possible to the Wall. I get the sign of [/color][/font][font='courier new'][color=rgb(34,34,34)]hspd[/color][/font][font=arial][color=rgb(34,34,34)], whether it is positive or negative and move Player one pixel, positive or negative along [/color][/font][font='courier new'][color=rgb(34,34,34)]x[/color][/font][font=arial][color=rgb(34,34,34)], until I hit Wall. The same [/color][/font][font='courier new'][color=rgb(34,34,34)]vspd[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]y[/color][/font][font=arial][color=rgb(34,34,34)]. That is all the movement.

For Attack, if [/color][/font][font='courier new'][color=rgb(34,34,34)]input_attack_pressed==true[/color][/font][font=arial][color=rgb(34,34,34)] then it creates the Weapon object at the Player's [/color][/font][font='courier new'][color=rgb(34,34,34)]x[/color][/font][font=arial][color=rgb(34,34,34)] and [/color][/font][font='courier new'][color=rgb(34,34,34)]y[/color][/font][font=arial][color=rgb(34,34,34)]. Weapon then destroys itself when its animation ends. That is all. Finally, the directions. Because the game will be on isometric, I would need to know which of the 8 directions the player is facing. All I do is divide [/color][/font][font='courier new'][color=rgb(34,34,34)]input_direction[/color][/font][font=arial][color=rgb(34,34,34)] by 45 and I get a number from 0 to 7, one for each direction. This value is stored in [/color][/font][font='courier new'][color=rgb(34,34,34)]dir[/color][/font][font=arial][color=rgb(34,34,34)]. this variable is used to rotate the sprite of Player and Weapon.

One [/color][/font]unique part [font=arial][color=rgb(34,34,34)]is the back button for Windows Phone, Right now it is used to quit the game because it has to. Later it will bring a menu, alongside the rest of the platforms. But I couldn't read the back button in code. it is mapped the backspace, so I should have been able to read the state of the backspace key and assume it was the back button on Windows Phone, but I couldn't. So right now it uses Game Maker's built in event for the Backspace key and sets [/color][/font][font='courier new'][color=rgb(34,34,34)]input_back[/color][/font][font=arial][color=rgb(34,34,34)] to true. Then, only on windows phone, if [/color][/font][font='courier new'][color=rgb(34,34,34)]input_back==true[/color][/font][font=arial][color=rgb(34,34,34)] then [/color][/font][font='courier new'][color=rgb(34,34,34)]game_end()[/color][/font][font=arial][color=rgb(34,34,34)].

One last thing are the [/color][/font][font='courier new'][color=rgb(34,34,34)]dbug[/color][/font][font=arial][color=rgb(34,34,34)] lines on the right side of the screen. I use this to view variables in real time or other thing, like if a block of code is being executed, or how a variable changes as it is being modified. [/color][/font][font='courier new'][color=rgb(34,34,34)]dbug[/color][/font][font=arial][color=rgb(34,34,34)] is an array of 28 items. I can assign any value to any of its elements and it will show up on the right side of the screen. I usually disabled this before releasing each prior alpha, but decided to keep it on this time. Additionally, the first line shows the platform, the current version, and a color, coded to the platform. I am planning to uses this as an incentive for paid users while the game is in development, along with other things that will be part of a "Dev View". On prior releases, I was also able to switch perspectives between isometric and Cartesian. Right now there is nothing isometric yet on the rewrite. That will be part of Dev View as well. But then there is the web version which is always free. And it would be nice for the Dev View to be on the web version as well, but it may remove the incentive to purchase it while in development. Well, we'll see

This was long. But it covered everything that was done in alpha 0.5.1. It was very reflective. On writing this I realize some things that I could change to make it better. For the next release, I plan to put back menus, screens, animations and add a few additional control elements. And it is the first week of February. One month 'till GDC, and the short month on that. Let's see how far I get. Maybe I should have made this post into parts.[/color][/font]

Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!