Animating/Moving CHANGEABLE weapons/armor through code? (equipping items) - PAPERDOLLING -


(John Andersson) #1

Hi.

Can anyone point me towards how to make a system where the player can “hold” and attack with weapons, animation-wise. So if the player picks up STAFF OF GUY then he will attack with that staff graphic. I already know how to work with spritemaps.

Same goes with armor, really. How to make it “stick” on the character and change dynamically? It feels like I just have to set the armor’s x and y values to follow the player. But how about the weapon? How do I make it so that it “follows” the hand of the character’s hand? I’m using sprite maps (sprite sheets), so I have no idea how to do this… if it was a movieclip in flash cs6 I could easily do it, but now we’re talking code only…

EDIT: READ BELOW. The problem is that the items follow the hero, but they are a bit slow, so they “follow” the hero instead of “staying on him”


(Zachary Lewis) #2

You can look into using an application like Spriter or Spine. These create a 2D skeleton that you can animate.


(John Andersson) #3

Sweet! I’ll check them out


(John Andersson) #4

Okay, so I checked it out, and it looks pretty awesome. But I am using very pixely pixel art. I think it’d be way too hard to implement that with Spriter or Spine. Is there any other way of doing this without using JSON files and skeletons etc?

Best regards


(Nate ) #5

I am not sure if I am understanding you correctly… but I would just have a massive spritesheet and create animations for each action that I wanted the player to do, then in the players update method simply change the animation with something like if(hasStaff){playerSprite.play(“staff”);}

This is how I would do it, which means it is probably wrong or outdated lol


(Per_K) #6

I think you could do something like this.

Decide what position the weapon should have for each frame in the animation of the character, compared to the top left corner of the frame.

You’ll need variables to hold that information.

var hotspot1X:int;
var hotspot1Y:int;

change the hotspot-variables according to which frame of the animation is playing.

if(animation.currentAnimation==”walk_right”)

{

                    if(animation.getFrame()==0)

                   {

                    hotspot1X=25;

                   hotspot1Y=20;

                   }

          else if(animation.getFrame()==1)
		
                  {
		
                    hotspot1X=20;

                  hotspot1Y=15;
		
                  }
	and so on …

} else if(animation.currentAnimation==”walk_left”)

{

}

set x and y values for the animation

and then for the weapon

weapon.x=animation.x + hotspot1X;

weapon.y=animation.y + hotspot1Y;


(Per_K) #7

oops.

currentAnimation should be currentAnim


(John Andersson) #9

Nice tip, I’ll use that for now! :slight_smile: thanks

PS: I tried using getFrame() as you typed, but it didn’t seem to work.

		if (sprHero.currentAnim == "run")
		{
			trace("running")
			if (sprHero.getFrame() == 0) wepHotspotY = 85; trace("0");
			if (sprHero.getFrame() == 1) wepHotspotY = 80; trace("1");
			if (sprHero.getFrame() == 2) wepHotspotY = 75; trace("2");
			if (sprHero.getFrame() == 3) wepHotspotY = 70; trace("3");
			if (sprHero.getFrame() == 4) wepHotspotY = 70; trace("4");
			if (sprHero.getFrame() == 5) wepHotspotY = 70; trace("5");
			if (sprHero.getFrame() == 6) wepHotspotY = 70; trace("6");
			if (sprHero.getFrame() == 7) wepHotspotY = 80; trace("7");
			if (sprHero.getFrame() == 8) wepHotspotY = 85; trace("8");
		}

It traces 0 through 8 on every frame whilst he is running. The running animation has 9 different frames itself.

I also tried changing it into this:

		if (sprHero.currentAnim == "run")
		{
			if (sprHero.index == 0) wepHotspotY = 85; trace("0");
			if (sprHero.index == 1) wepHotspotY = 80; trace("1");
			if (sprHero.index == 2) wepHotspotY = 75; trace("2");
			if (sprHero.index == 3) wepHotspotY = 70; trace("3");
			if (sprHero.index == 4) wepHotspotY = 70; trace("4");
			if (sprHero.index == 5) wepHotspotY = 70; trace("5");
			if (sprHero.index == 6) wepHotspotY = 70; trace("6");
			if (sprHero.index == 7) wepHotspotY = 80; trace("7");
			if (sprHero.index == 8) wepHotspotY = 85; trace("8");
		}

but that didn’t work. Which is weird, since I have an enemy who has his own punching animation and only if that punching animation has passed frame 6, then the hit will register.

		if (impCollision)
		{
			if (invincibilityCounter == 0)
			{
				if (impCollision.sprImp.currentAnim == "attack")
				{
					if (impCollision.sprImp.index >= 6)
					{
						var damageTaken:Number = Imp.impStrength * armorReduction;
						var xLocation:Number = x + this.width / 2 - 10;
						var a:Number = x + this.width / 2;
						var b:Number = Math.floor(Math.random() * (1 + y + 10 - y)) + y;
						
						hp -= damageTaken;
						invincibilityCountDown = true;
						
						FP.world.add(new DamageShower(a, b, damageTaken, "0xFFFFFF"));
					}
				}
			}
		}

That seems to work, but not the whole hotspot thing!


(John Andersson) #10

If only I knew how to make the weapon render as fast as the character, so it doesn’t look like it’s “following” the player :stuck_out_tongue:


(Per_K) #11

If you’re adding your hero to a world using

_hero= new Hero();

add (_hero);

I think you need to go

_hero.sprHero.index

(you’re right, it should be index not getFrame)

could that be the problem?


(John Andersson) #12

Hmm… The thing is that I’m doing this code inside the Hero class

EDIT: It seems to work now!? :S What the hell.

But, isn’t there a better way to do this? If I want some very complex animations, then I have to define hotspots for every one of them! Can’t I just tell the program to “snap” the weapon to the hand automatically in some way? Like image recognition for the hero, so it detects where the hand is (let’s say I make the holding hand a different color. Like green-screening!

Anyway, I still need to override the rendering so the sword isn’t 1 frame too late, which makes it look like it’s following the hero. How can I do this? D:


(Per_K) #13

If you, within a game loop,

  1. update the position of the hero and which frame is playing,
  2. update the hotSpot, and
  3. update the position of the weapon,
    in that order, the weapon should be beside the hand when the scene renders.

If you update in any other order the weapon will be at an old position when the scene is rendered. So that is probably what is happening here. You need to make sure that the three steps update in the correct order.

As for better ways of doing it, sorry, I don’t know. Marking the spot with a unique color might be a way to do it, but I don’t know how to make the program find that spot and identify its x and y. I’m sure it can be done though. I’m not that convinced that that will make things that much easier for you.

You might want to look into masking. There might be something you can do with that.


(John Andersson) #14

The thing is that the weapon has its own class…

package  
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.Mask;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
import net.flashpunk.graphics.Spritemap;
import net.flashpunk.graphics.Image;
import flash.geom.Point;
import net.flashpunk.FP;

public class Weapon_Sword_Basic extends Entity 
{
	[Embed(source = "assets/Weapon_Sword_Basic.png")] private const SWORD:Class;
	
	public var sprSword:Spritemap = new Spritemap(SWORD, 80, 80);
	
	private var attacking:Boolean = false;
	
	private var pickedUp:Boolean = false;
	
	public function Weapon_Sword_Basic(xt:Number, yt:Number) 
	{
		graphic = new Image(SWORD);
		layer = 1;
		setHitbox(80, 80)

		x = xt;
		y = yt;
		
		type = "weapon";
		
		graphic = sprSword;
		
		sprSword.add("idle", [0], 0, true);
	
		sprSword.add("attack", [0, 1, 2, 3, 4, 5, 6], 30, true);
		
		Input.define("Attack", Key.X);
	}
	
	override public function update():void 
	{
		var e:Hero = collide("hero", x, y) as Hero;
		
		//Make hero pick it up
		if (!pickedUp)
		{
			if (e)
			{
				pickedUp = true;
			}
		}
		
		if (pickedUp)
		{
		
			if (Hero.isFacingLeft)
			{
				sprSword.flipped = true;
			}else
			{
				sprSword.flipped = false;
			}
			
			x = Hero.wepHotspotX;
			                                               //Offset -.-
			y = Hero.yPos + Hero.wepHotspotY - this.height + 11;
		}
		
		//Make it go down
		if (!collide("ground", x, y))
		{
			if (!pickedUp)
			{
				y++;
			}
		}
		if (Input.pressed("Attack")) 
		{
			sprSword.play("attack");
			attacking = true;
			setHitbox(this.width, this.height)

		}
		
		if (attacking)
		{
			if (sprSword.frame == 6)
			{
				sprSword.play("idle");
				attacking = false;
			}
		}
	}
}

}


(Zachary Lewis) #15

In this case, it’s best just to have a dedicated character sprite for each weapon.


(John Andersson) #16

But since I have a lot of weapons and armor sets, that would mean I’d have to make over a hundred different character spritemaps…


(John Andersson) #17

“If only I knew how to make the weapon render as fast as the character, so it doesn’t look like it’s “following” the player”

can anyone help me with this? I know I have to override the render function, but I don’t know what to override it with… the weapon is always one frame behind, so it looks like it is floating


(David Williams) #18

(John Andersson) #19

Hey. I tried doing it, but it still doesn’t work that well. The items “stuck” on the hero are still not moving exactly the same time as the hero.

This is the hero code. Notice that the hotspot for X never changes since the character doesn’t move his hand back and forth during any animation. It is always on the same x position. The character bounces a bit as he runs, though.

In the loop:

		//Change weapon hotspots, depending on the frame/animation
		if (sprHero.currentAnim == "run")
		{
			if (sprHero.index == 0 || sprHero.index == 3)
			{
				wepHotspotY = yPos - 24; 
				helmetHotspotY = yPos - 24;
			}
			if (sprHero.index == 1 || sprHero.index == 2) 
			{
				wepHotspotY = yPos - 18; 
				helmetHotspotY = yPos - 18;
			}
			if (sprHero.index == 4)
			{
				wepHotspotY = yPos - 32;
				helmetHotspotY = yPos - 32;
			}
		}
		
		if (sprHero.currentAnim == "stand")
		{
			if (sprHero.index == 0) 
			{
				wepHotspotY = yPos - 24; 
				helmetHotspotY = yPos - 24;
			}
		}
		
		if (sprHero.currentAnim == "jump")
		{
			wepHotspotY = yPos - 24;
			helmetHotspotY = yPos - 24;
		}
		
		wepHotspotX = xPos + 104;
				
		// Update stuck objects.
		for each (var b:Weapon in _stuckEntities)
		{
			b.updateStuck(wepHotspotX, wepHotspotY);
		}

And the weapon:

	public function updateStuck(wepx:Number, wepy:Number):void
	{
		/*if (Hero.isFacingLeft)
		{
			x = wepx - 32;
			sprSword.flipped = true;
		}else
		{
			x = wepx;
			sprSword.flipped = false;
		}*/
		
		this.x = wepx;
		this.y = wepy;

		//Attack
		if (Input.pressed("Attack")) 
		{
			if (HeroStats.currentStamina == HeroStats.stamina)
			{
				HeroStats.currentStamina = 0;
				attacking = true;
			}
		}
		
		if (attacking)
		{
			sprSword.play("attack");
		}
		
		if (sprSword.currentAnim == "attack")
		{
			if (sprSword.index == 3)
			{
				attacking = false;
				sprSword.play("idle");
			}
		}
		
		super.update();
	}
	
}

(John Andersson) #20

I really need some help here, this is the only thing I’m having a big problem with at the current stage of the game. It looks horrible when the armor isn’t even “stuck” on the hero properly :frowning: It’s as if the paperdolling is working, but it’s… not…


(David Williams) #21

What’s the regular update function look like for the weapon class?