PDA

View Full Version : [AS3] Collison Detection Advice


aaron_da_killa
03-03-2009, 09:51 AM
Hey Guys.

I'm just after some advice for collision detection in my game. I currently have working collision detection implemented with gravity and horizontal/vertical scrolling and such however, I feel that I can better implement my collision detection.

At the moment, I have a function that is triggered every frame to test if the player has collided with any platforms, if the player has, it than triggers a function which handles interactions for that specific platform or object the player can collide with. The problem is I have to copy and paste this code and rename some of the strings every time I create a new platform and its tedious work and when my game is finished the code will be thousands of lines and 99% of that will be simply the copy and pasted code.

I can also see problems with the current method of collision detection if I want enemies to be able to move between platforms.

So what should I do? I'm fairly new to flash so I don't really know much (I don't even know how to put code in multiple as files, if somebody could tell me that too that'd be nice :p).

Note: It is possible for the player to be touching more than one platform at a time.

Here is an example of some code for collision detection for a platform.

This is inside a function triggered every frame:


if (player.hitTestObject(level2.platform)){
playerTouchingPlatform = true;
playerNotOnGround = false;
platformSit();
}
else {
playerTouchingPlatform = false;
}


And this is the function that is triggered if the player collides with level2.platform:


public function platformSit():void{
var platformPoint:Point = new Point(level2.platform.x, level2.platform.y);
platformPoint.y = level2.localToGlobal(platformPoint).y;
platformPoint.x = level2.localToGlobal(platformPoint).x;
//TOP SIDE
if (player.y + player.height <= platformPoint.y + level2.platform.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platform.width - 8){
playerJumping = false;
player.y = platformPoint.y - player.height;
playerTouchingPlatformSide = false;
}
//BOTTOM SIDE
else if (player.y >= platformPoint.y + level2.platform.height - level2.platform.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platform.width - 8){
jumpSpeed = 2;
playerTouchingPlatformSide = false;
}
//RIGHT SIDE
else if (player.x >= platformPoint.x + level2.platform.width - level2.platform.width/2 && player.y + player.height > platformPoint.y + 5){
mainSpeedLeft = 0;
playerTouchingPlatformSide = true;
}
//LEFT SIDE
else if ((player.x + player.width) <= (platformPoint.x + (level2.platform.width/2)) && player.y + player.height > platformPoint.y + 5){
mainSpeedRight = 0;
playerTouchingPlatformSide = true;
}
}


I've also uploaded my .as file if anybody wants to check it out. I don't have any code embedded in the timeline.

davidrlorentz
03-03-2009, 02:04 PM
This is the perfect application for classes, the touchstone of OOP. :cool:

Classes contain attributes and functions that all members of that class and its subclasses can use. So you need to define a class (call it "Platform" or "Collider" or what you will), add your collision functions to the class definition, and make all of your platforms members of that class.

This is where you need to make an external .as file. If you are using the Flash IDE, just go to File > New > ActionScript 3.0 File. This will create an .as file. Name it the same as the class - Platform.as - and save it in the same folder as your .fla. Insert the following code:


package
{
public class Platform extends Sprite
{

public function Platform()
{
//This is the constructor. Anything in here will be called
//when the platform is instantiated.
}

public function platformSit():void
{
//INSERT COLLISION CODE HERE.
}
}
}


That's the basic idea, anyway. Since this is a class definition, you'll also have to add import statements for any packages you use.

You then need to set each of your platforms to be a subclass of this Platform class. In your .fla file, right click on each type of platform in your Library. Select "Linkage Properties." Type something unique for the class - like PlatformRed, or whatever. For Base Class, type Platform (or whatever you named the class).

You can now remove all the code that you copied and pasted - all members of the Platform class will be using what's in the class definition. Make sense?

aaron_da_killa
03-04-2009, 12:19 AM
Whoa, thanks so much David! Although I'm quite confused :eek:. I have a few questions:

Do my platforms (the movieclips I will be using for this collision detection code) need instance names?

Type something unique for the class - like PlatformRed, or whatever.

Do each of my platform movieclips need to have the same class name, so if for one platform I put "PlatformRed" do they all need to be named "PlatformRed"?

How should I modify my collision detection code posted in my first post (second block of code) for my new class? That code specifically relates to the movieclip level2.platform. Should I replace level2.platform with PlatformRed??

I'm just still a little confused, I've never properly used a class and don't really understand how they work well.

Anyway, here is what my new class looks like so far:


package
{
import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip
import flash.display.Sprite
import flash.media.Sound;
import flash.geom.Transform
import flash.geom.Point

public class CollisonDetection extends MovieClip
{

public function CollisonDetection()
{
//This is the constructor. Anything in here will be called
//when the platform is instantiated.
}

public function platformSit():void
{
var platformPoint:Point = new Point(level2.platform.x, level2.platform.y);
platformPoint.y = level2.localToGlobal(platformPoint).y;
platformPoint.x = level2.localToGlobal(platformPoint).x;
//TOP SIDE
if (player.y + player.height <= platformPoint.y + level2.platform.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platform.width - 8){
playerJumping = false;
player.y = platformPoint.y - player.height;
playerTouchingPlatformSide = false;
}
//BOTTOM SIDE
else if (player.y >= platformPoint.y + level2.platform.height - level2.platform.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platform.width - 8){
jumpSpeed = 2;
playerTouchingPlatformSide = false;
}
//RIGHT SIDE
if (player.x >= platformPoint.x + level2.platform.width - level2.platform.width/2 && player.y + player.height > platformPoint.y + 5){
mainSpeedLeft = 0;
playerTouchingPlatformSide = true;
}
//LEFT SIDE
else if ((player.x + player.width) <= (platformPoint.x + (level2.platform.width/2)) && player.y + player.height > platformPoint.y + 5){
mainSpeedRight = 0;
playerTouchingPlatformSide = true;
}
}
}
}
}

aaron_da_killa
03-04-2009, 06:43 AM
Double Post:

Damn, I'm confused. I have another question. How do I call a function that is in another class?

davidrlorentz
03-05-2009, 02:00 AM
Well, keep in mind the distinction between classes and instances. The platforms you placed on the stage are INSTANCES of a class, meaning they all utilize the code in your class definition (the .as file). PlatformRed is the name of the class; the instance names are what you use to reference them specifically. So if your instance name before was level2.platform, leave it that way. You will probably need to reference the instance names of all your platforms in your hit detection function.

When you are writing the platformSit() function in your class definition, keep in mind that every INSTANCE of platform in your game will be running a "copy" of this code. So instead of using "level2.platform.x", for instance, use "this.x". The "this" keyword references whatever specific instance is running the code. Hence, it's reusable. :)

davidrlorentz
03-05-2009, 02:04 AM
Double Post:

Damn, I'm confused. I have another question. How do I call a function that is in another class?

Well again if you want to call a function on a specific INSTANCE of a class, you need to do instanceName.functionName() - so, like, level2.platform.platformSit(), for instance.

You will never refer to a class name specifically in your code, unless you are either constructing a new instance of the class, or referencing static methods or variables of the class - a whole other topic.

aaron_da_killa
03-05-2009, 02:41 AM
What my biggest problem is is how do I now initiate the collision detection?

I just want to make it clear that before I posted this thread, ALL my code was contained inside a single *.as file, now I have two *.as files one with all my code except collision detection, and one with my collision detection code. In my original *.as file, I have a function that listens for collision detection and than triggers platformSit() which is in my other class. However I have two problems:

1) I can't work out how to detect collision without an instance name. (player.hitTestObject(WHATDOIPUTHERE). I obviously can't use instance names as I can't have more than one movieclip with the same instance name or else the collision detection doesn't work properly.

2) If I fix up the collision detection using an instance name and trigger platformSit() I get an error saying that it is an undefined method. Remember platformSit is in my CollisonDetection.as (other class).

And I'm still not exactly sure what to put as class and base class. My thinking is now I put class as CollisonDetection and base class as PlayerMovement (my original class). Anyway, let me show you what I have now.

This is in PlayerMovement.as


public function ifplatformCollide(event:Event):void{

if (player.hitTestObject(level2.platformDefineBox)){
playerTouchingPlatform = true;
playerNotOnGround = false;
//var colDet = new CollisonDetection();
//colDet.platformSit();
platformSit();
}
else {
playerTouchingPlatform = false;
}
}



This is CollisonDetection.as (I will replace level2.platf... with "this")


package {
import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.media.Sound;
import flash.geom.Transform;
import flash.geom.Point;

public class CollisonDetection extends PlayerMovement {

public function CollisonDetection() {
//This is the constructor. Anything in here will be called
//when the platform is instantiated.
}

public function platformSit():void {
level2.tehBackground.y = 99999999;
/*var platformPoint:Point = new Point(level2.platformDefineBox.x, level2.platformDefineBox.y);
platformPoint.y = level2.localToGlobal(platformPoint).y;
platformPoint.x = level2.localToGlobal(platformPoint).x;
//TOP SIDE
if (player.y + player.height <= platformPoint.y + level2.platformDefineBox.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platformDefineBox.width - 8) {
playerJumping = false;
player.y = platformPoint.y - player.height;
playerTouchingPlatformSide = false;
}
//BOTTOM SIDE
if (player.y >= platformPoint.y + level2.platformDefineBox.height - level2.platformDefineBox.height/2 && player.x + player.width > platformPoint.x + 8 && player.x < platformPoint.x + level2.platformDefineBox.width - 8) {
jumpSpeed = 2;
playerTouchingPlatformSide = false;
}
//RIGHT SIDE
if (player.x >= platformPoint.x + level2.platformDefineBox.width - level2.platformDefineBox.width/2 && player.y + player.height > platformPoint.y + 5) {
mainSpeedLeft = 0;
playerTouchingPlatformSide = true;
}
//LEFT SIDE
if ((player.x + player.width) <= (platformPoint.x + (level2.platformDefineBox.width/2)) && player.y + player.height > platformPoint.y + 5) {
mainSpeedRight = 0;
playerTouchingPlatformSide = true;
}*/
}
}
}


I also temporarily renamed my platform movieclip to platformDefineBox.

Thanks David for the help, I'm glad you came back to give me some more insight, it seems nobody else is interested :(. I'm pretty new to actionscript and I've never used a class in any programming language so I'm very confused, I guess I need somebody to spell it out for me.

aaron_da_killa
03-05-2009, 05:44 AM
Double Post

Ok, ignore my last post, I just need the following questions answered:

1) Does each platform movieclip on the stage need its own unique instance name?

2) When I go right click > linkage on the platform movieclip in the library, what should I make "class". If I use "class"s name in the code, where would I use it?

3) When I go right click > linkage on the platform movieclip in the library, what should I make "base class". If I use "base class"s name in the code, where would I use it?

What I mean with the last two questions is say I make base class "hello", is "hello" then supposed to appear somewhere else in my code in the .as file, if so then where?

Thanks for your support mate ;). I'm just really really really confused with this.

davidrlorentz
03-05-2009, 06:40 PM
1) Does each platform movieclip on the stage need its own unique instance name?

Yep. In your collision detection function, if you are doing a hitTest on each platform, you are going to need to reference the platforms specifically, by instance name.


2) When I go right click > linkage on the platform movieclip in the library, what should I make "class". If I use "class"s name in the code, where would I use it?

It really won't matter what you put for "class." You should use Platform or any word that describes the specific library object.
For "base class" you need to use CollisionDetection.
The way this works is that the class is a subclass (i.e., a child class) of the base class.


What I mean with the last two questions is say I make base class "hello", is "hello" then supposed to appear somewhere else in my code in the .as file, if so then where?

The way your code is set up, the class of the platform won't appear anywhere in your .as files. And the ONLY place where CollisionDetection (the base class) will be mentioned is in its own definition, CollisionDetection.as. Remember, whenever you need to refer to specific objects in your game, you need to use their instance names.

In order to make sense of all this, you simply need to read up on the principles of object oriented programming. It doesn't even need to be about AS3 specifically - just find any reference about OOP generally, and you'll be up and running in no time. Maybe you can start here (http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000058.html).

aaron_da_killa
03-05-2009, 10:43 PM
Ah, thanks so much David, I really appreciate your help mate :). I think I'm starting to get an understanding now!

Now I don't have my code with me at the moment but for memory, my class definition line:

public class CollisonDetection extends PlayerMovement

What should I make for the last bit "extends PlayerMovement". Currently, I have PlayerMovement (that being the name of my original class which is my main class) there but I have a feeling it should be "extends MovieClip" looking at the examples I have seen but if I make it "extends MovieClip" it gives me a compile error for both PlayerMovement.as and CollisonDetection.as. I'd like CollisonDetection.as to inherit all the variables defined in PlayerMovement.as if possible so I don't know...

Edit:

I've been messing around with my code, you know, I have a sneaking suspicion that somehow my CollisonDetection.as isn't "linked up" to my PlayerMovement.as or my fla file. Is there somewhere I need to define the new file in PlayerMovement.as or a setting I need to change or something in my fla file?

Here is what my current CollisonDetection file looks like:


package {
public class CollisonDetection extends PlayerMovement {

import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.media.Sound;
import flash.geom.Transform;
import flash.geom.Point;

public function CollisonDetection() {
stage.addEventListener(Event.ENTER_FRAME, playerCollideWithPlatform);
level2.tehBackground.y = 9999;

}

public function playerCollideWithPlatform(event:Event):void {
if (player.hitTestObject(platform001)) {

}
}
}
}


As you can see I've heavily simplified that and in theory, this like:


level2.tehBackground.y = 9999;


should be initiated straight away but it isn't. Could this be because I haven't done the whole linkage, make a class and base class thing? In other words, every movieclip I want to use in CollisonDetection, I must do the linkage thing and set the base class to CollisonDetection?

Just to let ya know, Ive been doing a little bit of reading on classes and I think I understand them, there an example of encapsulation right? Anyway, I think my problem lies it not knowing how to properly utilize a class in ActionScript 3.0. I did some brief work with classes a few years ago in c++ when I was doing some code for Half-Life 1.

aaron_da_killa
03-07-2009, 10:39 AM
Alright, I feel I am close to this working! As you can see in my last post (before this one), I defined the class before the import statements :rolleyes:. One problem stands in my way now, I get this runtime error:


TypeError: Error #1009: Cannot access a property or method of a null object reference.
at PlayerMovement$iinit()
at CollisonDetection$iinit()
at sss$iinit()
at flash.display::Sprite/flash.display:Sprite::constructChildren()
at flash.display::Sprite$iinit()
at flash.display::MovieClip$iinit()
at Untitlegrd_fla::level_1$iinit()
at flash.display::Sprite/flash.display:Sprite::constructChildren()
at flash.display::Sprite$iinit()
at flash.display::MovieClip$iinit()
at PlayerMovement$iinit()


I'm certain it has something to do with the platform or the CollisonDetection class because when I place two platforms on the stage I get this error twice, also the background is not disappearing (or rather moving its y axis to 9999; this should happen in the CollisonDetection class, I did this as a test to check weather the CollisonDetection class is being initiated).

Is there anything you forgot to mention to me, do I need to add child or say new platform or something in my code somewhere? Apparently this error is caused by me referencing an object with null properties, or in other words, an object that isn't on the stage.

Her is my current settings:

Platform Linkage Settings

Class: PlatformDefineBox
Base Class: CollisonDetection

Platform Instance Name

Instance Name: platform001 // there is currently only one platform movieclip on the stage

FLA Settings

Document Class: PlayerMovement

CollisonDetection Class


package {

import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.media.Sound;
import flash.geom.Transform;
import flash.geom.Point;

public class CollisonDetection extends PlayerMovement {

public function CollisonDetection() {
stage.addEventListener(Event.ENTER_FRAME, playerCollideWithPlatform);
level2.tehBackground.y = 9999;


}

public function playerCollideWithPlatform(event:Event):void {
if (player.hitTestObject(level2.platform001)) {

}
}
}
}

davidrlorentz
03-09-2009, 04:01 PM
Hey, well, CollisionDetection shouldn't extend your document class - it should extend MovieClip. This might be the source of your compile error.

If your platforms are placed on the stage, there's nothing else you need to do in code - placing them there is equivalent to constructing them in code.