I had the opportunity to playtest Carrier over Thanksgiving break with my family, and I thought I'd talk about that for a while.
None of them are gamers, so I was worried that the playtests wouldn't go well - when we playtested at school, we were able to show people not only the current state of the game, but also what we wanted the final game to be like. The people that were looking at the game had experience with the language of what we were trying to do, so they could offer fairly logical advice.
That wasn't necessarily the case with my Thanksgiving crowd. Fortunately, there are some advantages to the other approach. Because my family doesn't play many games, they don't take as many things for granted. I found out that different mechanics I had thought were obvious, weren't.
It was useful. Mechanically, I want to slow the helecopter, and the game in general down a little bit, to make it easier to control. People also commented on scaling issues when flying too high, so that's something to look into as well.
Carrier Design Blog
Tuesday, December 3, 2013
Friday, November 8, 2013
Next steps
Over the past week, our team was able to playtest movement and collisions for Carrier. We came out of it with a better perspective on how, among other things, movement and physics ought to work.
Our initial plan was to calculate acceleration and speed based on the proximity of the user's finger to the helicopter. The user could move with precision when necessary, and move quickly if they needed to get away from something, or build up speed in order to throw an object.
In practice, what we found was that people always used the max acceleration (finger on the corner of the screen). We also got a lot of feedback from those same testers complaining about both the speed of the helicopter and the speed of the acceleration.
What seemed to be happening was that users expected the helicopter to almost disregard acceleration. I had expected them to mostly tap close to the helicopter and maintain a small speed with small accelerations, similar to how a rocketship might navigate through space with thrusters. With that in mind, if we slow down the maximum speed and raise acceleration, that may result in a better control scheme.
Additionally, since users are disregarding anything but the maximum amount of acceleration, our team has decided to move to a fixed acceleration, rather than one based on distance. This should lend itself more to predictable movement, and allow people to tap closer to the helicopter and use less motion to control it.
Our second playtest is in two weeks, where we'll test the revised physics out to see if there's been any improvement.
Our initial plan was to calculate acceleration and speed based on the proximity of the user's finger to the helicopter. The user could move with precision when necessary, and move quickly if they needed to get away from something, or build up speed in order to throw an object.
In practice, what we found was that people always used the max acceleration (finger on the corner of the screen). We also got a lot of feedback from those same testers complaining about both the speed of the helicopter and the speed of the acceleration.
What seemed to be happening was that users expected the helicopter to almost disregard acceleration. I had expected them to mostly tap close to the helicopter and maintain a small speed with small accelerations, similar to how a rocketship might navigate through space with thrusters. With that in mind, if we slow down the maximum speed and raise acceleration, that may result in a better control scheme.
Additionally, since users are disregarding anything but the maximum amount of acceleration, our team has decided to move to a fixed acceleration, rather than one based on distance. This should lend itself more to predictable movement, and allow people to tap closer to the helicopter and use less motion to control it.
Our second playtest is in two weeks, where we'll test the revised physics out to see if there's been any improvement.
Friday, November 1, 2013
Collision Detection and How Rectangles Mess Everything Up
We're moving close to the stage where we'll have actual gameplay, by our playtest date we'll have enemies and more interesting object interactions. Objects can destroy each other, and soon they'll have health and masses to determine how much damage they do when hurtled into other blunt objects.
One thing I've been thinking about is collision detection. Flash has a couple of different methods for dealing with this, and we're going with standard bounding box collision. Which is great, if all of your objects are rectangular, but ours aren't. So there are a couple of solutions we can pursue.
1. We can get our artist to draw everything in a rectangle. Which could be a valid solution, I've worked on games on the past, including Eyes Open, where we pretty much did this. With Carrier though, we have really large objects, including buildings, and it just seems a little too short-sighted.
2. We can come up with another method for doing collision detection that will give our artist some flexibility, which is what I'm going to try and do, hopefully before Tuesday. Objects have their own isColliding method, which means that depending on the game object, we can swap out different collision methods, and still just return true and false whenever we check collisions. For complex objects, I plan on inserting multiple rectangles into the parent clip and then looping through them to handle collisions.
This is a good example of why it's handy to abstract methods out to individual classes. If we had hard coded our original collision detection instead of letting objects handle it, it would be a serious pain to try and recode this. But because all the game objects are capable of making their own decisions, and just default back to the parent when they don't have anything to say, and because the engine constantly asks them if they want to make their own decisions instead of either requiring them to, or defaulting to the parent itself, we can do interesting overrides like this.
One thing I've been thinking about is collision detection. Flash has a couple of different methods for dealing with this, and we're going with standard bounding box collision. Which is great, if all of your objects are rectangular, but ours aren't. So there are a couple of solutions we can pursue.
1. We can get our artist to draw everything in a rectangle. Which could be a valid solution, I've worked on games on the past, including Eyes Open, where we pretty much did this. With Carrier though, we have really large objects, including buildings, and it just seems a little too short-sighted.
2. We can come up with another method for doing collision detection that will give our artist some flexibility, which is what I'm going to try and do, hopefully before Tuesday. Objects have their own isColliding method, which means that depending on the game object, we can swap out different collision methods, and still just return true and false whenever we check collisions. For complex objects, I plan on inserting multiple rectangles into the parent clip and then looping through them to handle collisions.
This is a good example of why it's handy to abstract methods out to individual classes. If we had hard coded our original collision detection instead of letting objects handle it, it would be a serious pain to try and recode this. But because all the game objects are capable of making their own decisions, and just default back to the parent when they don't have anything to say, and because the engine constantly asks them if they want to make their own decisions instead of either requiring them to, or defaulting to the parent itself, we can do interesting overrides like this.
Saturday, October 26, 2013
Git is awesome
We've made some good progress over the last week. Movement code is properly working now, the player can pick up and throw objects, and we have other fancy mechanics like a ground.
What we're going to be working towards before Tuesday is to bring all of that together with collision detection and destruction. Objects will react to each other, and when they collide, they'll have sophisticated conversations about what their mass and velocity is and which one of them should explode.
All in all, I'm really happy with where we are. The team seems to be working together really well, and we've started to get past the awkward stage where nobody wants to touch other people's code, so over the past week a lot of progress has been made. I think we're in a good position.
The point of this post though is to give a word of advice. When we started this project, we collectively decided that since not all of us knew how to use Git repositories, we'd just coordinate over Google Drive. This was a really big mistake; right now we're manually merging files, we have a sign-out sheet so we don't accidentally undo each others work, and we're maintaining manual backups of all of our versions - all of this is stuff that Git would be doing for us automatically.
I speak for pretty much everyone on our team when I say that managing group repositories on Google Drive is a nightmare. Learning Git is really worth it, even if it seems inconvenient in the short run.
What we're going to be working towards before Tuesday is to bring all of that together with collision detection and destruction. Objects will react to each other, and when they collide, they'll have sophisticated conversations about what their mass and velocity is and which one of them should explode.
All in all, I'm really happy with where we are. The team seems to be working together really well, and we've started to get past the awkward stage where nobody wants to touch other people's code, so over the past week a lot of progress has been made. I think we're in a good position.
The point of this post though is to give a word of advice. When we started this project, we collectively decided that since not all of us knew how to use Git repositories, we'd just coordinate over Google Drive. This was a really big mistake; right now we're manually merging files, we have a sign-out sheet so we don't accidentally undo each others work, and we're maintaining manual backups of all of our versions - all of this is stuff that Git would be doing for us automatically.
I speak for pretty much everyone on our team when I say that managing group repositories on Google Drive is a nightmare. Learning Git is really worth it, even if it seems inconvenient in the short run.
Friday, October 18, 2013
Parallax effects for game objects
This week I worked more on physics and throwing objects. While they're great for the player, the problem I've run into with fake physics is that the systems rarely play nice together - because the systems aren't following real rules, the rules they do have often contradict each other.
I wanted to fix some of this, so I redid the systems to be more internally consistent. The rope winch has its own acceleration system, momentum system, velocity, and most importantly, wind resistance.
Wind resistance is important because it's normally left out of physics simulations with Box2D, but it adds a level of intuitiveness to ours. If you throw a ball out of a moving car, theoretically you could get it to bounce along side, but you never expect it to - you want it to trail off behind you. By including wind resistance as a variable (based off of what the velocity of the helicopter is), we can get semi-realistic trailing of the rope without directly effecting the rope's underlying velocity.
I've also been playing around with parallax effects and how that effects objects l. Originally, we had distinct layers that were positioned alongside the camera. However, we didn't want to nest objects that would be interacting with the game world in a separate clip. The solution to that problem ended up being adding them to a separate blank tracker during runtime. That way, all of our references still worked, but we could also drag around a layer and treat it like a single object.
I wanted to fix some of this, so I redid the systems to be more internally consistent. The rope winch has its own acceleration system, momentum system, velocity, and most importantly, wind resistance.
Wind resistance is important because it's normally left out of physics simulations with Box2D, but it adds a level of intuitiveness to ours. If you throw a ball out of a moving car, theoretically you could get it to bounce along side, but you never expect it to - you want it to trail off behind you. By including wind resistance as a variable (based off of what the velocity of the helicopter is), we can get semi-realistic trailing of the rope without directly effecting the rope's underlying velocity.
I've also been playing around with parallax effects and how that effects objects l. Originally, we had distinct layers that were positioned alongside the camera. However, we didn't want to nest objects that would be interacting with the game world in a separate clip. The solution to that problem ended up being adding them to a separate blank tracker during runtime. That way, all of our references still worked, but we could also drag around a layer and treat it like a single object.
Friday, October 11, 2013
Flash Scaling and Converting Coordinate Systems.
This week I'm going to be moving back from architecture and camera development to the physics systems again. I'm working over a way to use air resistance to help make the game's logic a bit more internally consistent.
In the meantime, you may recall that I talked about Flash coordinate systems very briefly last week. The tricky thing when doing conversions is figuring out just how little Flash cares about what's done to its parent clip. I want to reiterate that principle this week and give some specific instances where that's likely to bite you.
For example, if you resize a movie clip in Flash, the internal coordinate system of that clip stays exactly the same. That means if you're doing conversions manually, you need to multiply all of your final values by the scale.
Rotation is another point where Flash doesn't care - a rotated movie clip will internally have no change in it's coordinate system whatsoever. This can, again, lead to problems if you rotate an object with a lot of children by 90 degrees - when referencing that object's children, your x axis and y axis will be switched, because the children don't know that they've been rotated.
The best way I know of to think about this stuff is to imagine a parent clip as a lens for its children. The parent clip can be manipulated in a number of ways, and that manipulation will show itself in the children. But the children themselves are never manipulated. They just look like they have been, because they're being viewed through the lens of the parent clip.
This is another reason why localToGlobal is so ridiculously useful. It worries about that stuff for you. I can't emphasize enough how much time you can save by using those functions instead of doing the math yourself.
In the meantime, you may recall that I talked about Flash coordinate systems very briefly last week. The tricky thing when doing conversions is figuring out just how little Flash cares about what's done to its parent clip. I want to reiterate that principle this week and give some specific instances where that's likely to bite you.
For example, if you resize a movie clip in Flash, the internal coordinate system of that clip stays exactly the same. That means if you're doing conversions manually, you need to multiply all of your final values by the scale.
Rotation is another point where Flash doesn't care - a rotated movie clip will internally have no change in it's coordinate system whatsoever. This can, again, lead to problems if you rotate an object with a lot of children by 90 degrees - when referencing that object's children, your x axis and y axis will be switched, because the children don't know that they've been rotated.
The best way I know of to think about this stuff is to imagine a parent clip as a lens for its children. The parent clip can be manipulated in a number of ways, and that manipulation will show itself in the children. But the children themselves are never manipulated. They just look like they have been, because they're being viewed through the lens of the parent clip.
This is another reason why localToGlobal is so ridiculously useful. It worries about that stuff for you. I can't emphasize enough how much time you can save by using those functions instead of doing the math yourself.
Friday, October 4, 2013
Camera Controls in Flash
I was going to work more on the physics system, but we ran into some issues with our camera class, so I took some time to figure that out. There's a little bit left to do, but it seems to be working well so far. The big problem with Flash cameras is that coordinate systems become overly complicated. Flash uses local coordinate systems, so objects embedded within other objects reset what their 0,0 position is.
There are a couple of ways to fix this.
point = clip.localToGlobal(point); //This code will allow you to convert a local coordinate system to the actual position on screen.
localToGlobal has some issues in that it always defaults to the top layer. So if you have an object nested down three layers, and you want it's position on the first layer, you run into issues.
If you can, you want all of your objects to be working on the same layer, that way you don't have to deal with that huge mess.
We have a single camera class that doubles as our world class. All of the code for the game acts in this movie clip, so we don't need to worry about conversions. When we do need to worry about conversions, I reference back to the world class so that, again, all of our code is running in the same coordinate system and we don't need to worry about too much math. I'm not going to go into too much detail, but I use both localToGlobal and then the reverse method globalToLocal to get the coordinates. Because all of the game objects have references to the world class, we can use that as an interpreter for everyone's different positions. All any one individual object needs to worry about is letting the world class know where it is, and making sure it goes through the world class whenever it references any other object's position.
In terms of the camera (world) class itself, we have fairly standard controls. The camera can grab any movieClip and center itself on that clip. Internally, it re-centers every update loop. We can also set scale, etc... although most of that code will probably be written directly within the game's core update loop, rather than in a separate method.
The camera itself doesn't use localToGlobal. It manually calculates the changes in coordinate systems. The problem is that localToGlobal changes as the camera adjusts itself. Doing the manual conversions provides a lot more consistency.
There's still a lot to do - looping the world around, deciding how objects are updated, etc... still needs to be coded. But we have a good base to do that now.
Finally, I'm experimenting with parallax effects. I don't have everything up and running yet, but what the camera should be able to do eventually is grab different layers (background, foreground) and adjust them accordingly. From what I've been seeing with my experiments, having objects and their backgrounds move at the exact same pace is somewhat disorienting, so I'm pretty keen on trying to get that working before too long.
There are a couple of ways to fix this.
point = clip.localToGlobal(point); //This code will allow you to convert a local coordinate system to the actual position on screen.
localToGlobal has some issues in that it always defaults to the top layer. So if you have an object nested down three layers, and you want it's position on the first layer, you run into issues.
If you can, you want all of your objects to be working on the same layer, that way you don't have to deal with that huge mess.
We have a single camera class that doubles as our world class. All of the code for the game acts in this movie clip, so we don't need to worry about conversions. When we do need to worry about conversions, I reference back to the world class so that, again, all of our code is running in the same coordinate system and we don't need to worry about too much math. I'm not going to go into too much detail, but I use both localToGlobal and then the reverse method globalToLocal to get the coordinates. Because all of the game objects have references to the world class, we can use that as an interpreter for everyone's different positions. All any one individual object needs to worry about is letting the world class know where it is, and making sure it goes through the world class whenever it references any other object's position.
In terms of the camera (world) class itself, we have fairly standard controls. The camera can grab any movieClip and center itself on that clip. Internally, it re-centers every update loop. We can also set scale, etc... although most of that code will probably be written directly within the game's core update loop, rather than in a separate method.
The camera itself doesn't use localToGlobal. It manually calculates the changes in coordinate systems. The problem is that localToGlobal changes as the camera adjusts itself. Doing the manual conversions provides a lot more consistency.
There's still a lot to do - looping the world around, deciding how objects are updated, etc... still needs to be coded. But we have a good base to do that now.
Finally, I'm experimenting with parallax effects. I don't have everything up and running yet, but what the camera should be able to do eventually is grab different layers (background, foreground) and adjust them accordingly. From what I've been seeing with my experiments, having objects and their backgrounds move at the exact same pace is somewhat disorienting, so I'm pretty keen on trying to get that working before too long.
Subscribe to:
Comments (Atom)