Game Development Community

Melee Attacks in a platformer

by Adrian Leigh Gordon · in Torque Game Builder · 06/18/2009 (9:29 am) · 5 replies

Hi all,

I'm working on a small platformer, and i'm trying to get my character to perform melee attacks with a weapon, the only problem is i'm not sure how to handle it.

Firstly is the animation, the animation for the attack is wider than the walk/stand animation, and when I tried to setPositionX(%this.getPositionX + %offset) it jumps about all over the place instead of moving it smoothly to and from the location.

The other problem is collision, because it's not generating a new object, like a projectile would be, i've no idea how to handle this, would I need to change or animate the collision polygon? If so how do I go about that? If not, how would you suggest I check whether or not the attack hits an enemy standing in front of the player?

Cheers
Adrian

#1
06/18/2009 (9:05 pm)
Hey Adrian. Sorry I don't have an exact answer for you but your problem reminds me of some stuff I've been working on. I can't quite figure out what's going on from how you've described the animation problem but the first thing I'd check is whether or not your animations line up correctly without moving the position. If by jumps about all over the place you mean that the attack and walk/stand cycles are nowhere near each other then I'd look at the contents of the offset and the results of the getPositionX() call and check if they're what you expect them to be.

As to your second problem my understanding is that you're going to have to manually describe the motions of the weapon to TGB, as it doesn't use masks or any means to tell where one pixel or another is. I haven't tried this, but what comes to mind is using one or more t2dSceneObjects with collision detection enabled. You would have to move the objects in time with the animation. If you had a sword with an overhead chop, for instance, you could create a t2dSceneObject and rotate it as the sword moves. But you'd have to line it up and move it frame by frame.

If you have a more simple attack, like a sword that sweeps through the same area each time in front of the player, you could try mounting a t2dSceneObject to your player and turning it's collision on only when the player is attacking.

Another way to do it would be if you know the area you want to check for collisions you could use one of the pick calls (pickLine, pickPoint, pickRect etc.) that are part of t2dSceneGraph and look for what's in the area. But you'd have to inspect the ids of what gets returned each time to see if there was an enemy, then handle the interaction outside of collision detection.

Hope that gives you some ideas.
#2
06/18/2009 (11:54 pm)
If you go down the path of using pickLine (which I highly recommend), I suggest that you start using Layer or Graph Groups. In short, they will significantly improve the performance of a "pick" method.

Here is a quick snippet I just whipped up. It is completely untested:
$Game::EnemyGroup  = -1;
$Game::PunchRange  = 2;
$Game::PunchDamage = 20;
function MyPlayer::punch( %this )
{
    // Find Enemies in Melee Range.
    %objectList  = %this.findMeleeEnemies( $Game::PunchRange );
    %objectCount = getWordCount( %objectList );
    
    if ( %objectCount == 0 )
    {
        // No Enemies to Punch!
        return false;
    }
    
    // Iterate over Enemies in Melee Range.
    for ( %i = 0; %i < %objectCount; %i++ )
    {
        // Fetch Object.
        %object = getWord( %objectList, %i );
        
        // Fetch Behavior.
        %behavior = %object.getBehavior( TakesDamageBehavior );
        
        // Can Take Damage?
        if ( !isObject( %behavior ) )
        {
            continue;
        }
        
        // Take Damage!
        %behavior.takeDamage( $Game::PunchDamage, %this );
    }
}

function MyPlayer::findMeleeEnemies( %this, %pickRange )
{
    %sceneGraph = %this.getSceneGraph();
    if ( !isObject( %sceneGraph ) )
    {
        error( "MyPlayer::findMeleeEnemies() - Not in a valid Scene Graph." );
        return "";
    }
    
    // Local Space Length.
    %localLength = %pickRange / ( %this.getWidth() / 2 );
    
    // Flipped?
    if ( !%this.getFlipX() )
    {
        %pickStart = %this.getWorldPoint( 1.0, 0.0 );
        %pickEnd   = %this.getWorldPoint( 1.0 + %localLength, 0.0 );
    }
    
    // Nope!
    else
    {
        %pickStart = %this.getWorldPoint( -1.0, 0.0 );
        %pickEnd   = %this.getWorldPoint( -( 1.0 + %localLength ), 0.0 );
    }
    
    // Return Pick.
    return %sceneGraph.pickLine( %pickStart, %pickEnd, $Game::EnemyGroup, -1, true, %this );
}
Now, obviously this isn't as good as a "hitbox" solution, as Aaron was describing, but it is a quick and easy solution. It also depends on how your game is played, if you were going for something like Castle Crashers, then the above code would be quite alright. If you are making something like Street Fighter, then you'd need a much more robust solution.

In the above code, I have given the enemy group a value of -1 to select all groups. You would need to change this to the BIT of the group you assign your enemies. If you had them in group 4, you would do this:
$Game::EnemyGroup = bit( 4 );
If you had them in either group 4 or 5, then you would do this:
$Game::EnemyGroup = bit( 4 ) | bit( 5 );

Good luck!
#3
06/20/2009 (4:46 am)
Wow, thanks so much for the responses guys they've given me some brilliant ideas, I never knew about the pick functions either so those open up a whole new world for me :D

I'll begin implementing a solution today, and Phillip O'shea: it's gonna be more in tune with castle crashers, without the up/down movement, think more sort of castlevania style platformer.

One more little question I had though, if I wanted multiple weapons for example, and used the method that attached a box to the player where the hit would take place and turn on/off collision appropriattely, would an effective method of switching out weapons be to delete that box then load an appropriatly sized one to replace it? If so, how would I go about loading and positioning a sceneobject via code? Do the setSizeX/Y and setPosition commands work the same for an invisible object as they do for one with an image?

Sorry if that sounds noobish haha, only be programming 9 months :) mostly java, i'm trying to gain as much experience over the summer as possible on hopes of gaining a placement year with a nearby developer.
#4
06/20/2009 (4:53 am)
Sounds like you're running into the same problem I'm having.

There doesn't seem to be any easy way to have variable weapons and armor for a 2D player. What I plan to do is make the object create child objects when it spawns, mount them to the object, and create animations with the rotate object function.

If you only plan on having only a couple of different weapons however, you could just make a few similar animations.


If anyone has a better solution, I'd like to hear it too :D
#5
06/21/2009 (4:32 am)
I just found a simple solution :D

%this.setLinkPoint();
It can change the location of link points, so a mounted object can move with an animation.

I'll be posting code on the thread I started:
http://www.garagegames.com/community/forums/viewthread/94317