Page 1 of 2

Making a "hitbox" follow the player?

Posted: Tue Sep 23, 2014 3:56 am
by Bya
I'm trying to make a "hitbox" for my character's attack (making a platformer). I want the hitbox object to "follow" the player and be "bound" to it, but I'm not entirely sure how to do that. My player is coded as follows

Code: Select all

player = {}
	player.b = love.physics.newBody(world, 400, 200, "dynamic")
	player.b:setMass(10)
	player.s = love.physics.newRectangleShape(32,54)
	player.f = love.physics.newFixture(player.b, player.s)
	player.f:setRestitution(0.4)
	player.f:setUserData("Knight")
	player.x = 50;
	player.y = 50;
I attempted to code the hitbox similarly like a rectangle object, such as

Code: Select all

swordhitbox = {}
		swordhitbox.b = love.physics.newBody(world, player.b, player.s, "static")
		swordhitbox.s = love.physics.newRectangleShape(25,25)
		swordhitbox.f = love.physics.newFixture(swordhitbox.b, swordhitbox.s)
		swordhitbox.f:setUserData("swordhitbox")
But it doesn't work, likely because I tried substituting player.b and player.s for the x and y coordinates. I figured that by putting in what (I assume to be) the x and y coordinates for the player, that the hitbox would always follow the player. Additionally, how would I make it so that this hitbox only exists when a key is pressed down?

Re: Making a "hitbox" follow the player?

Posted: Tue Sep 23, 2014 4:07 am
by Blank
I'm not too keen on how LUA works but this works in other languages. Couldn't you do--

Code: Select all

swordhitbox.x = player.x
swordhitbox.y = player.y

Re: Making a "hitbox" follow the player?

Posted: Tue Sep 23, 2014 9:23 pm
by Bya
Blank wrote:I'm not too keen on how LUA works but this works in other languages. Couldn't you do--

Code: Select all

swordhitbox.x = player.x
swordhitbox.y = player.y
Yeah, I don't think that would really do anything. player.x and player.y are values that I actually forgot to remove a while ago.

Re: Making a "hitbox" follow the player?

Posted: Wed Sep 24, 2014 9:15 am
by artofwork
Upload the love file so we can see how we can help you :)

Re: Making a "hitbox" follow the player?

Posted: Wed Sep 24, 2014 11:53 pm
by Bya
My love file doesn't consist of anything other than my main lua file (below) and the sprites I need for the game.

Code: Select all

function love.load()

	playerimage = love.graphics.newImage("GraveStone.png")
	playerrun = love.graphics.newImage("KnightSprites/KnightRunRight.gif")
	playerattack = love.graphics.newImage("KnightSprites/KnightStab.gif")
	playerjump = love.graphics.newImage("KnightSprites/KnightJump.gif")
	playerdamaged = love.graphics.newImage("KnightSprites/KnightDamaged.gif")
	playerrun1 = love.graphics.newImage("KnightSprites/KnightRunRight1.png")
	playerrun2 = love.graphics.newImage("KnightSprites/KnightRunRight2.png")
	brick = love.graphics.newImage("Brick.png")
	playertesting = love.graphics.newImage("GraveStone.png")
	playeridle = love.graphics.newImage("KnightSprites/KnightIdle1.png")
	
	classselect = 0
	-- 1 = Knight
	-- 2 = Fighter
	-- 3 = Rogue
	-- 4 = Sorceror
	
	classselecting = 1
	-- a boolean on whether or not the player is picking a character. 1 is on, 0 is off. Default on.
	
	permaselect = 0
	-- a boolean on whether or not the player has selected a character
	
	classchosen = 0
	--same 1-4 values as classselect
	images = {
	run_right = love.graphics.newImage("GraveStone.png"),
    testing = love.graphics.newImage("GraveStone.png"),
    run_right2 = love.graphics.newImage("GraveStone.png"),
    damaged = love.graphics.newImage("GraveStone.png"),
    jump = love.graphics.newImage("GraveStone.png"),
    idle = love.graphics.newImage("GraveStone.png"),
    stab = love.graphics.newImage("GraveStone.png"),
	idle2 = love.graphics.newImage("GraveStone.png"),
	}
	
	
	
	healthicon = love.graphics.newImage("healthicon.png")
	healthbit = love.graphics.newImage("healthbiticon.png")
	knightattackbox = love.graphics.newImage("KnightSwordHitBox.gif")
	
	cursor = love.graphics.newImage("Arrow.png")
	tile1 = love.graphics.newImage("KnightSelectStats.png")
	tile2 = love.graphics.newImage("FighterSelectStats.png")
	tile3 = love.graphics.newImage("RogueSelectStats.png")
	tile4 = love.graphics.newImage("SorcerorSelectStats.png")
	--playerrun1 = love.graphics.newImage("KnightRunRight1.png")
	--playerrun2 = love.graphics.newImage("KnightRunRight2.png")
	--playeridle1 = love.graphics.newImage("KnightIdle1.png")
	--playeridle2 = love.graphics.newImage("KnightIdle2.png")
	
	
	
	tempenemyimage = love.graphics.newImage("BadGuy1.png")
	--x = 50
	--y = 50
	speed = 300
	health = 5
	
	enemyhealth = 1
	--physics engine below
	love.physics.setMeter(64)
	world = love.physics.newWorld(0, 200, true)
	world:setCallbacks(beginContact, endContact, preSolve, postSolve)
	text = ""
	persisting = 0
	
	
	
	player = {}
	player.b = love.physics.newBody(world, 400, 200, "dynamic")
	player.b:setMass(10)
	player.s = love.physics.newRectangleShape(32,54)
	player.f = love.physics.newFixture(player.b, player.s)
	player.f:setRestitution(0.4)
	player.f:setUserData("Knight")
	player.x = 50;
	player.y = 50;
	
	enemy = {}
	enemy.b = love.physics.newBody(world, 300, 200, "dynamic")
	enemy.b:setMass(10)
	enemy.s = love.physics.newCircleShape(20)
	enemy.f = love.physics.newFixture(enemy.b, enemy.s)
	enemy.f:setRestitution(0.4)
	enemy.f:setUserData("Enemy")
	
	static = {}
		static.b = love.physics.newBody(world, 325, 600, "static")
		static.s = love.physics.newRectangleShape(650,1)
		static.f = love.physics.newFixture(static.b, static.s)
		static.f:setUserData("Floor")
	
	--swordhitbox = {}
		--swordhitbox.b = love.physics.newBody(world, player.b, player.s, "static")
		--swordhitbox.s = love.physics.newRectangleShape(25,25)
		--swordhitbox.f = love.physics.newFixture(swordhitbox.b, swordhitbox.s)
		--swordhitbox.f:setUserData("swordhitbox")
		
	leftwall = {}
	leftwall.b = love.physics.newBody(world, 0, 325, "static")
	leftwall.s = love.physics.newRectangleShape(5, 650)
	leftwall.f = love.physics.newFixture(leftwall.b, leftwall.s)
	leftwall.f:setUserData("LeftWall")
	
	
	rightwall = {650, 325}
	rightwall.b = love.physics.newBody(world, 650, 325, "static")
	rightwall.s = love.physics.newRectangleShape(5, 650)
	rightwall.f = love.physics.newFixture(rightwall.b, rightwall.s)
	rightwall.f:setUserData("RightWall")
	
	ceiling = {}
	ceiling.b = love.physics.newBody(world, 325, 0, "static")
	ceiling.s = love.physics.newRectangleShape(650, 5)
	ceiling.f = love.physics.newFixture(ceiling.b, ceiling.s)
	ceiling.f:setUserData("Ceiling")
	love.graphics.setBackgroundColor(104, 136, 248)
	love.window.setMode(650,650)
	end
function beginContact(a, b, coll)
    if a == player.f then
		--text = text.."?????"
		if b == enemy.f then
			text = text.."CONTACT"
			playerimage = images.damaged
			--playerimage = love.graphics.newImage("KnightIdle.gif")
			health = health - 1
			end
			end
			
	x,y = coll:getNormal() 
    text = text.."\n"..a:getUserData().." colliding with "..b:getUserData().." with a vector normal of: "..x..", "..y
end

function beginPlayerEnemyContact(player, enemy, coll)
	x,y = coll:getNormal()
	text = "!!!!!!!"
	text = text.."\n"..a:getUserData().." uncolliding with "..b:getUserData()
end
function endPlayerEnemyContact(player, enemy, coll)
persisting = 0
text = text.."\n"..a:getUserData().." uncolliding with "..b:getUserData()
end
	
function endContact(a, b, coll)
persisting = 0
text = text.."\n"..a:getUserData().." uncolliding with "..b:getUserData()
end

function preSolve(a, b, coll)
    if persisting == 0 then    -- only say when they first start touching
        text = text.."\n"..a:getUserData().." touching "..b:getUserData()
    elseif persisting < 20 then    -- then just start counting
        text = text.." "..persisting
    end
    persisting = persisting + 1    -- keep track of how many updates they've been touching for
end

function postSolve(a, b, coll, normalimpulse1, tangentimpulse1, normalimpulse2, tangentimpulse2)
end

maxTime = 0.2
curTime = 0
function love.update(dt)
curTime = curTime + dt
world:update(dt)

	if (curTime > maxTime) then
		if playerimage == images.run_right then
			playerimage = images.run_right2
			curTime = curTime - maxTime
		
		elseif playerimage == images.run_right2 then
			playerimage = images.run_right
			curTime = curTime - maxTime
		
		elseif playerimage == images.damaged then
			playerimage = images.idle2
			curTime = curTime - maxTime
		elseif playerimage == images.idle then
			playerimage = images.idle2
			curTime = curTime - maxTime
			
		elseif playerimage == images.idle2 then
			playerimage = images.idle
			curTime = curTime - maxTime
		end
	end
if health < 1 then
playerimage = playertesting
end


if love.keyboard.isDown("right") then
        player.b:applyForce(500, 0)
		playerimage = images.run_right
		
    elseif love.keyboard.isDown("left") then
        player.b:applyForce(-500, 0)
		playerimage = images.run_right
    end
    if love.keyboard.isDown("up") then
        player.b:applyForce(0, -1000)
		enemy.b:applyForce(0, -100)
		playerimage = images.jump
    elseif love.keyboard.isDown("down") then
        player.b:applyForce(0, 50)
		playerimage = images.idle
    end
	if love.keyboard.isDown("x") then
		playerimage = images.stab
	end
	if love.keyboard.isDown("1") then
		classselect = 1
	end
	if love.keyboard.isDown("2") then
		classselect = 2
	end
	if love.keyboard.isDown("3") then
		classselect = 3
	end
	if love.keyboard.isDown("4") then
		classselect = 4
	end
	if love.keyboard.isDown("return") then
		permaselect = 1
	end
	
	
	--if beginContact(player, enemy,)
		--then text = "!!!!!"
		--end
if string.len(text) > 768 then
text = ""

--What you want to do is check if a or b (by :getUserData()) is a spcific object (PlayerEnemy)
end

-- the below section changes the default player to their actual class
if classchosen == 1 then
	images = {
	run_right = love.graphics.newImage("KnightSprites/KnightRunRight1.png"),
    run_right2 = love.graphics.newImage("KnightSprites/KnightRunRight2.png"),
    damaged = love.graphics.newImage("KnightSprites/KnightDamaged.gif"),
    jump = love.graphics.newImage("KnightSprites/KnightJump.gif"),
    idle = love.graphics.newImage("KnightSprites/KnightIdle1.png"),
    stab = love.graphics.newImage("KnightSprites/KnightStab.gif"),
	idle2 = love.graphics.newImage("KnightSprites/KnightIdle2.png"),
	}
	end
	
	if classchosen == 2 then
	images = {
	run_right = love.graphics.newImage("FighterSprites/FighterRunRight1.png"),
    run_right2 = love.graphics.newImage("FighterSprites/FighterRunRight2.png"),
    damaged = love.graphics.newImage("FighterSprites/FighterDamaged.png"),
    jump = love.graphics.newImage("FighterSprites/FighterJump.png"),
    idle = love.graphics.newImage("FighterSprites/FighterIdle1.png"),
    stab = love.graphics.newImage("FighterSprites/FighterPunch.png"),
	idle2 = love.graphics.newImage("FighterSprites/FighterIdle2.png"),
	}
	end
	
	if classchosen == 3 then
	images = {
	run_right = love.graphics.newImage("RogueSprites/RogueRun1.png"),
	run_right2 = love.graphics.newImage("RogueSprites/RogueRun2.png"),
	damaged = love.graphics.newImage("RogueSprites/RogueDamaged.png"),
	jump = love.graphics.newImage("RogueSprites/RogueJump.png"),
	idle = love.graphics.newImage("RogueSprites/RogueIdle1.png"),
	idle2 = love.graphics.newImage("RogueSprites/RogueIdle2.png"),
	stab = love.graphics.newImage("RogueSprites/RogueStab.png"),
	}
	end
	
	if classchosen == 4 then
	images = {
	run_right = love.graphics.newImage("SorcerorSprites/SorcerorRun1.png"),
	run_right2 = love.graphics.newImage("SorcerorSprites/SorcerorRun2.png"),
	damaged = love.graphics.newImage("SorcerorSprites/SorcerorDamaged.png"),
	jump = love.graphics.newImage("SorcerorSprites/SorcerorJump.png"),
	idle = love.graphics.newImage("SorcerorSprites/SorcerorIdle1.png"),
	idle2 = love.graphics.newImage("SorcerorSprites/SorcerorIdle2.png"),
	stab = love.graphics.newImage("SorcerorSprites/SorcerorStab.png"),
	}
	end
	end
function love.draw()
	love.graphics.print(text, 100, 10)
	love.graphics.draw(healthicon, 10, 10)

	if health == 5 then
	love.graphics.draw(healthbit, 30, 10)
	love.graphics.draw(healthbit, 40, 10)
	love.graphics.draw(healthbit, 50, 10)
	love.graphics.draw(healthbit, 60, 10)
	love.graphics.draw(healthbit, 70, 10)
end
if health == 4 then
	love.graphics.draw(healthbit, 30, 10)
	love.graphics.draw(healthbit, 40, 10)
	love.graphics.draw(healthbit, 50, 10)
	love.graphics.draw(healthbit, 60, 10)
end

if health == 3 then
	love.graphics.draw(healthbit, 30, 10)
	love.graphics.draw(healthbit, 40, 10)
	love.graphics.draw(healthbit, 50, 10)
end
if health == 2 then
	love.graphics.draw(healthbit, 30, 10)
	love.graphics.draw(healthbit, 40, 10)
end

if health == 1 then
	love.graphics.draw(healthbit, 30, 10)
end

if classselecting == 1 then
	love.graphics.draw(tile1, 0, 100)
	love.graphics.draw(tile2, 0, 210)
	love.graphics.draw(tile3, 0, 320)
	love.graphics.draw(tile4, 0, 430)
		if classselect == 1 then
			love.graphics.draw(cursor, 100, 100)
				if permaselect == 1 then
					classchosen = 1
				end
		end
		if classselect == 2 then
			love.graphics.draw(cursor, 100, 210)
			if permaselect == 1 then
					classchosen = 2
				end
		end
		if classselect == 3 then
			love.graphics.draw(cursor, 100, 320)
			if permaselect == 1 then
					classchosen = 3
				end
		end
		if classselect == 4 then
			love.graphics.draw(cursor, 100, 430)
			if permaselect == 1 then
					classchosen = 4
				end
		end
		
end

	love.graphics.draw(playerimage, player.b:getX(),player.b:getY(), 0, 1, 1, playerimage:getWidth()/2, playerimage:getHeight()/2)
	love.graphics.draw(tempenemyimage, enemy.b:getX(),enemy.b:getY(), 0, 1, 1, tempenemyimage:getWidth()/2, tempenemyimage:getHeight()/2)
	love.graphics.polygon("line", static.b:getWorldPoints(static.s:getPoints()))
	love.graphics.polygon("line", leftwall.b:getWorldPoints(leftwall.s:getPoints()))
	love.graphics.polygon("line", rightwall.b:getWorldPoints(rightwall.s:getPoints()))
	love.graphics.polygon("line", ceiling.b:getWorldPoints(ceiling.s:getPoints()))
	--love.graphics.draw(brick,  static.b:getWorldPoints(static.s:getPoints()))
	
	
	
end

Re: Making a "hitbox" follow the player?

Posted: Thu Sep 25, 2014 9:19 am
by MadByte
what about:

Code: Select all

function love.update(dt)
  swordhitbox.b:setPosition(player.b:getPosition())
end
It's important that the position gets updated constantly.

Re: Making a "hitbox" follow the player?

Posted: Mon Sep 29, 2014 7:49 am
by Bya
MadByte wrote:what about:

Code: Select all

function love.update(dt)
  swordhitbox.b:setPosition(player.b:getPosition())
end
It's important that the position gets updated constantly.
This worked! I have it set that the hitbox only sticks to the player when they hit the attack button, which works, but I didn't realize that having a physics object tied to a player would have some consquences.

Image

Is there a way to make the hitbox entity not collide with only the player? Or to make it intangible, but still be able to check when it "collides" with something, just not to actually physically interact like it does in the gif.

Re: Making a "hitbox" follow the player?

Posted: Mon Sep 29, 2014 8:21 am
by Plu
Yeah, there are two basic ways. The simple one is to turn the hitbox into a sensor:

http://leafo.net/love/wiki/fixture_setsensor1.html

This will make the hitbox not resolve collisions but it will still trigger callbacks (ie; you can detect when it hits and stops hitting something, but it won't push anything around)

The complicated one is to use Categories and Masks; this allows you to build a hitbox that only reacts to some things; useful if you want the sword to bash enemies away but not the player. For that, I refer you to this article which explains it fairly well:

http://www.iforce2d.net/b2dtut/collision-filtering

Re: Making a "hitbox" follow the player?

Posted: Mon Sep 29, 2014 8:35 am
by Bya
Plu wrote:Yeah, there are two basic ways. The simple one is to turn the hitbox into a sensor:

http://leafo.net/love/wiki/fixture_setsensor1.html

This will make the hitbox not resolve collisions but it will still trigger callbacks (ie; you can detect when it hits and stops hitting something, but it won't push anything around)

The complicated one is to use Categories and Masks; this allows you to build a hitbox that only reacts to some things; useful if you want the sword to bash enemies away but not the player. For that, I refer you to this article which explains it fairly well:

http://www.iforce2d.net/b2dtut/collision-filtering
With the code

Code: Select all

swordhitbox = {}
		swordhitbox.b = love.physics.newBody(world, 10000, 10000, "static")
		swordhitbox.s = love.physics.newRectangleShape(25,25)
		swordhitbox.f = love.physics.newFixture(swordhitbox.b, swordhitbox.s)
		swordhitbox.f:setUserData("swordhitbox")
		swordhitbox.f:setSensor(playerhitboxsensor)
It doesn't really change at all, unless I also need to change it from "static" to something else? Or when I invoke

Code: Select all

swordhitbox.b:setPosition(player.b:getPosition())
When the attack button is pressed, I should use playerhitboxsensor instead of swordhitbox?

Re: Making a "hitbox" follow the player?

Posted: Mon Sep 29, 2014 10:40 am
by Robin
setSensor takes a boolean, btw, I don't know what playerhitboxsensor is...

Also, setPosition is... iffy. You could use a [wiki]MouseJoint[/wiki], which connects a body to a point in the world that you can update.