Page 1 of 1

I'm looking for help with making bullets in LÖVE

Posted: Wed Dec 23, 2015 10:52 pm
by The Lie
So I tried making bullets for a game by creating bodies and shapes and putting them into a table on keypress. After this step I wanted to put them together by a fixture. When I tried pressing the button though, löve told me that either the body was a shape or the shape was a body. Which was wrong dependet on what I put into the table first. The code in question:

Code: Select all

if love.keyboard.isDown('kp6') then
	if sniggle == 1 then
	newShape = love.physics.newRectangleShape( 2, 2)
	table.insert(objects.bullets, newShape)
	newBody = love.physics.newBody(world, objects.player.body:getX(), objects.player.body:getY())
	table.insert(objects.bullets, newBody)
	
	sniggle = sniggle-1
	end
	else
	sniggle = 1

	for i, Body in ipairs(objects.bullets) do

	
	for j, Shape in ipairs(objects.bullets) do
	
	if i == j and i > 0  then
	newFixture = love.physics.newFixture(Body, Shape, 0)
	table.insert(objects.bullets, newFixture)
	Body:setLinearVelocity(400,0)
	end
	
	end

	end
	
	end
Main.lua
My Main.lua for additional info if necessary
(5.02 KiB) Downloaded 149 times
thanks in advance for any help :3

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 5:38 am
by giantofbabil
It looks like you are making the bullet shape before making the body. A shape is attached to a body by a fixture. Look at your other physics objects when you make them it goes Body->Shape->Fixture.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 10:29 am
by The Lie
giantofbabil wrote:It looks like you are making the bullet shape before making the body. A shape is attached to a body by a fixture. Look at your other physics objects when you make them it goes Body->Shape->Fixture.
First, thanks for the help. But If I change the order, the error just switches. when I write:

Code: Select all

newBody = love.physics.newBody(world, objects.player.body:getX(), objects.player.body:getY())
newShape = love.physics.newRectangleShape( 2, 2)
table.insert(objects.bullets, newBody)
table.insert(objects.bullets, newShape)
It says that it got a body when it expected a shape.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 11:04 am
by bartbes
If we presume you insert the shape first, and then the body, and we rewrite the nested ipairs to a print, like this:

Code: Select all

for i, Body in ipairs(objects.bullets) do
    for j, Shape in ipairs(objects.bullets) do
        print(i, j, Body, Shape)
    end
end
We'll see the output is:

Code: Select all

1, 1, Shape, Shape
1, 2, Shape, Body
2, 1, Body, Shape
2, 2, Body, Body
As you may now realise, the nested ipairs must not be doing what you expect them to, since this always means both 'Body' and 'Shape' will have a value of the other type at some point.

The best way to fix this is to not store the two together, perhaps by storing more structured data instead:

Code: Select all

local body = love.physics.newBody(world, objects.player.body:getX(), objects.player.body:getY())
local shape = love.physics.newRectangleShape(2, 2)
table.insert(objects.bullets, {body = body, shape = shape})

for i, bullet in ipairs(objects.bullets) do
    bullet.fixture = love.physics.newFixture(bullet.body, bullet.shape, 0)
    bullet.body:setLinearVelocity(400, 0)
end
Of course since you probably don't want to recreate fixtures for every bullet whenever a new one is fired, you're probably better off not using a loop just calling newFixture once.

As some extra explanation, the names given in the for loop for ipairs are not filters of any sort. They simply represent the variable names used for the iteration values.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 5:12 pm
by giantofbabil
bartbes wrote:Of course since you probably don't want to recreate fixtures for every bullet whenever a new one is fired, you're probably better off not using a loop just calling newFixture once.
A little confused by this, if he has multiple bullets on the screen at the same time doesn't each bullet need a fixture? That way they can be set as sensors to detect collision? I'm building a system like this right now as well about to start digging into some source to try to find something similar because putting in bullets and enemies is much more complicated when they are physics based and no tutorials seem to cover it :P

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 6:49 pm
by bartbes
giantofbabil wrote: A little confused by this, if he has multiple bullets on the screen at the same time doesn't each bullet need a fixture?
Yes, but you don't need to create a fixture for every bullet every time you add one bullet, you only need to create the fixture for that bullet.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 7:32 pm
by giantofbabil
I'm trying to do the same thing as OP with this and having an error when I press 'f'(my fire button) that says:
Error

assets/loaders/weapons.lua:49: attempt to index field 'body' (a nil value)

Code: Select all

function weaponsLoad()

 local sprite = map.layers['Sprite Layer'].sprites.player
 --set up table for Zapper
 sprite.zapperGun = {canShoot         = true,
                  canShootTimerMax = 0.2,
                  canShootTimer    = 0.2,
                  zapImg           = love.graphics.newImage('assets/particles/roundBlue.png'),
                  zapSound         = love.audio.newSource('assets/sound/serglaser.wav', 'static'),
                  zapBullet        = {}}

end

function weaponsUpdate(dt)

  --timer for Zapper shot separation
  local sprite = map.layers['Sprite Layer'].sprites.player
  if playerStats.weaponEquip == 0 then
    sprite.zapperGun.canShootTimer = sprite.zapperGun.canShootTimer - (1 * dt)
    if sprite.zapperGun.canShootTimer <= 0 then
      sprite.zapperGun.canShoot = true
    end
  end

  --fire button response
  local isDown = love.keyboard.isDown
  if isDown('f') and playerStats.weaponEquip == 0 and sprite.zapperGun.canShoot == true then
    --create zapper shot
    local body  = love.physics.newBody(world, sprite.body:getX(), sprite.body:getY(), 'dynamic')
    local shape = love.physics.newRectangleShape(0, 0, 15, 15, 0)
    table.insert(sprite.zapperGun.zapBullet, {body, shape})
    sprite.zapperGun.zapSound:play()
    sprite.zapperGun.canShoot = false
    sprite.zapperGun.canShootTimer = sprite.zapperGun.canShootTimerMax
  end


  --update position for zapper shots
  if playerStats.weaponEquip == 0 then
    for i, bullet in ipairs(sprite.zapperGun.zapBullet) do
      if sprite.lastdirection == 'right' then
        bullet.fixture = love.physics.newFixture(bullet.body, bullet.shape)
        bullet.body:setLinearVelocity(500, 0)
      elseif sprite.lastdirection == 'left' then
        bullet.fixture = love.physics.newFixture(bullet.body, bullet.shape)
        bullet.body:setLinearVelocity(-500, 0)
      end
      if bullet.body:getX() < 0 or bullet.body:getX() > 7680 then
        table.remove(sprite.zapperGun.zapBullet, i)
      end
    end
  end

end

function weaponsDraw(dt)

  local sprite = map.layers['Sprite Layer'].sprites.player
  --update position for zapper shots
  if playerStats.weaponEquip == 0 then
    for i, bullet in ipairs(sprite.zapperGun.zapBullet) do
      love.graphics.setColor(0, 255, 0, 255)
      love.graphics.draw(sprite.zapperGun.zapImg, bullet.body:getX(), bullet.body:getY(), 0, 0.5, 0.5)
    end
  end

end
Github: https://github.com/NeverHuman/Space-Chow.love

I'm using STI so I'm trying to create the bullet in a custom layer called 'Sprite Layer', I'm sure there's probably a syntax error or something... I'm guessing to fix the fixture thing you were talking about I would have to add fixture to the table.insert so it would be table.insert{body, shape, fixture} right?

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 7:45 pm
by bartbes
The actual error is that you're inserting {body, shape}, where it should be {body = body, shape = shape}, the first is equivalent to {[1] = body, [2] = shape} instead.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 8:20 pm
by The Lie
bartbes wrote:If we presume you insert the shape first, and then the body, and we rewrite the nested ipairs to a print, like this:

Code: Select all

for i, Body in ipairs(objects.bullets) do
    for j, Shape in ipairs(objects.bullets) do
        print(i, j, Body, Shape)
    end
end
We'll see the output is:

Code: Select all

1, 1, Shape, Shape
1, 2, Shape, Body
2, 1, Body, Shape
2, 2, Body, Body
As you may now realise, the nested ipairs must not be doing what you expect them to, since this always means both 'Body' and 'Shape' will have a value of the other type at some point.

The best way to fix this is to not store the two together, perhaps by storing more structured data instead:

Code: Select all

local body = love.physics.newBody(world, objects.player.body:getX(), objects.player.body:getY())
local shape = love.physics.newRectangleShape(2, 2)
table.insert(objects.bullets, {body = body, shape = shape})

for i, bullet in ipairs(objects.bullets) do
    bullet.fixture = love.physics.newFixture(bullet.body, bullet.shape, 0)
    bullet.body:setLinearVelocity(400, 0)
end
Of course since you probably don't want to recreate fixtures for every bullet whenever a new one is fired, you're probably better off not using a loop just calling newFixture once.

As some extra explanation, the names given in the for loop for ipairs are not filters of any sort. They simply represent the variable names used for the iteration values.
Wow, thanks for all the work especially on christmas eve, have happy holidays.

Re: I'm looking for help with making bullets in LÖVE

Posted: Thu Dec 24, 2015 9:02 pm
by giantofbabil
The Lie wrote:Wow, thanks for all the work especially on christmas eve, have happy holidays.
If it helps at all my bullet code is now working, the github link is above if you want to look at the whole code for my engine but keep in mind you may need different values than me my game is a platformer and I have physics and variables flying around like crazy lol. Anyway here's my working weapon/bullet code:

Code: Select all

function weaponsLoad()
  
  local sprite = map.layers['Sprite Layer'].sprites.player
  --set up table for Zapper
  sprite.zapperGun = {canShoot         = true,
                      canShootTimerMax = 0.2,
                      canShootTimer    = 0.2,
                      zapImg           = love.graphics.newImage('assets/particles/roundBlue.png'),
                      zapSound         = love.audio.newSource('assets/sound/serglaser.wav', 'static'),
                      zapBullet        = {}}

end

function weaponsUpdate(dt)
  
  --timer for Zapper shot separation
  local sprite = map.layers['Sprite Layer'].sprites.player
  if playerStats.weaponEquip == 0 then
    sprite.zapperGun.canShootTimer = sprite.zapperGun.canShootTimer - (1 * dt)
    if sprite.zapperGun.canShootTimer <= 0 then
      sprite.zapperGun.canShoot = true
    end
  end
  
  --fire button response
  local isDown = love.keyboard.isDown
  if isDown('f') and playerStats.weaponEquip == 0 and sprite.zapperGun.canShoot == true then
    --create zapper shot
    local body    = love.physics.newBody(world, sprite.body:getX(), sprite.body:getY(), 'dynamic')
    local shape   = love.physics.newRectangleShape(7, 7, 13, 13, 0)
    local fixture = love.physics.newFixture(body, shape)
    table.insert(sprite.zapperGun.zapBullet, {body = body, shape = shape, fixture = fixture:setSensor(true), body:setBullet(true), speedSet = false})
    sprite.zapperGun.zapSound:play()
    sprite.zapperGun.canShoot = false
    sprite.zapperGun.canShootTimer = sprite.zapperGun.canShootTimerMax
  end
  
  --update position for zapper shots
  if playerStats.weaponEquip == 0 then
    for i, bullet in ipairs(sprite.zapperGun.zapBullet) do
      if sprite.lastDirection == 'right' and bullet.speedSet == false then -- THIS MIGHT NOT WORK
        bullet.body:setLinearVelocity(2.5 * physicsMultiplier, -0.1 * physicsMultiplier)
        bullet.speedSet = true
      elseif sprite.lastDirection == 'left' and bullet.speedSet == false then
        bullet.body:setLinearVelocity(-2.5 * physicsMultiplier, -0.1 * physicsMultiplier)
        bullet.speedSet = true
      end
      if bullet.body:getX() < 0 or bullet.body:getX() > 7680 then
        table.remove(sprite.zapperGun.zapBullet, i)
      elseif bullet.body:getY() < 0 or bullet.body:getY() > 4352 then
        table.remove(sprite.zapperGun.zapBullet, i)
      end
    end
  end
  
end

function weaponsDraw(dt)
  
  local sprite = map.layers['Sprite Layer'].sprites.player
  --update position for zapper shots
  if playerStats.weaponEquip == 0 then
    for i, bullet in ipairs(sprite.zapperGun.zapBullet) do
      love.graphics.setColor(0, 255, 0, 255)
      love.graphics.draw(sprite.zapperGun.zapImg, bullet.body:getX(), bullet.body:getY(), 0, 0.5, 0.5)
      love.graphics.setColor(255, 255, 255, 255)
    end
  end
  
end
I'm going to modify this to include multiple weapons, ammo, and add animations as well :) but for now enemies is next on my list.

Happy holidays!