Making a "hitbox" follow the player?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Bya
Prole
Posts: 20
Joined: Sat Sep 06, 2014 4:24 am

Making a "hitbox" follow the player?

Post 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?
User avatar
Blank
Prole
Posts: 2
Joined: Mon Sep 22, 2014 2:12 pm

Re: Making a "hitbox" follow the player?

Post 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
Bya
Prole
Posts: 20
Joined: Sat Sep 06, 2014 4:24 am

Re: Making a "hitbox" follow the player?

Post 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.
User avatar
artofwork
Citizen
Posts: 91
Joined: Mon Sep 15, 2014 1:17 am
Location: East Coast USA

Re: Making a "hitbox" follow the player?

Post by artofwork »

Upload the love file so we can see how we can help you :)
Bya
Prole
Posts: 20
Joined: Sat Sep 06, 2014 4:24 am

Re: Making a "hitbox" follow the player?

Post 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
User avatar
MadByte
Party member
Posts: 533
Joined: Fri May 03, 2013 6:42 pm
Location: Braunschweig, Germany

Re: Making a "hitbox" follow the player?

Post 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.
Bya
Prole
Posts: 20
Joined: Sat Sep 06, 2014 4:24 am

Re: Making a "hitbox" follow the player?

Post 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.
User avatar
Plu
Inner party member
Posts: 722
Joined: Fri Mar 15, 2013 9:36 pm

Re: Making a "hitbox" follow the player?

Post 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
Bya
Prole
Posts: 20
Joined: Sat Sep 06, 2014 4:24 am

Re: Making a "hitbox" follow the player?

Post 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?
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Making a "hitbox" follow the player?

Post 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.
Help us help you: attach a .love.
Post Reply

Who is online

Users browsing this forum: Amazon [Bot] and 7 guests