Page 1 of 1

Need help with shotgun blast logic

Posted: Sat May 27, 2017 9:12 pm
by pikuchan
Hey everyone. I need some help and I have a feeling that either there is some fancy math that I need, or I am just overthinking it. I am making a smash tv type of overhead shooter where the player tracks the mouse and you click to fire a bullet in that direction. All of my rotation and shooting is working fine, even found some fancy math to determine the origin point. The problem that I am having is I am trying to make some different guns now and I want some type of a shotgun type of blast. When the player clicks, one bullet should go straight, and then I want additional bullets that spread out. It's pretty easy to get it going for one direction, like Contra or a flying shooter, but I can't seem to figure out the logic to determine what the players rotation is and make a decision based on that. Thanks in advance for any help.

Shoot logic (also detects the point of the gun

Code: Select all

if mouseDownTime >= rateOfFire then
      XofGun = player.x + (14 * math.cos(player.direction)) - (5 * math.sin(player.direction))
      YofGGun = player.y + (5 * math.cos(player.direction)) + (14 * math.sin(player.direction))

      local x = love.mouse.getX()
      local y = love.mouse.getY()
      CreateNewBullet(x, y, XofGun, YofGGun)

      mouseDownTime = 0
    end
Create bullet logic

Code: Select all

function CreateNewBullet(x, y, startX, startY)

  local bulletDx, bulletDy = CalculateDxDy(x, y, startX, startY)

  bulletBoundingBox = {x = startX - (bulletSize/2), y = startY - (bulletSize/2), w = bulletSize, h = bulletSize}

  table.insert(bullets, {x = startX, y = startY, dx = bulletDx, dy = bulletDy, bulletBoundingBox })
Bullet update. I don't think it's relevant, but just in case

Code: Select all

function BulletUpdate(dt)
  for i=#bullets, 1, -1 do
    bullets[i].x = bullets[i].x + (bullets[i].dx * dt)
    bullets[i].y = bullets[i].y + (bullets[i].dy * dt)

    bullets[i].bulletBoundingBox = {x = bullets[i].x - (bulletSize/2), y = bullets[i].y - (bulletSize/2), w = bulletSize, h = bulletSize}

    if CheckBulletPosition(bullets[i]) == true then
      table.remove(bullets, i)
    end
  end
end

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 9:26 pm
by raidho36
Compute the angle towards your target, and for each shoot pellet add a small random angle. Or a fixed specific angle for each individual pellet if you want uniform repeatable pattern.

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 9:30 pm
by zorg
So, as you said, all of your rotation and shooting is working fine; so, the logic behind spread shots would be simply this:
- Spawn more than one bullet
- From the "base" angle of the player, have a spread parameter (in degrees or radians, whichever you use already) and a fragment count parameter (or two if you want to randomize the count between two values) for your type of gun;
Then, in a loop, from 1 to a random amount between min and max fragment count, spawn projectiles that have their angles be the base angle plus/minus some random amount between 0 and the spread parameter.

This is one way of doing it; try to code it yourself, it really helps! :3

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 9:38 pm
by pikuchan
hmmmmm....I've tried manipulating the mouseX and mouseY origin points and i've also tried manipulating the bullets.dx and bullets.dy values and I can't quite get the end result that I am looking for, maybe I need to change my create bullet logic. I should have posted the code for that calculation as well, it is

Code: Select all

function CalculateDxDy(x, y, startX, startY)
  local mouseX = x
  local mouseY = y

  local angle = math.atan2((mouseY - startY), (mouseX - startX))

  local bulletDx = bulletSpeed * math.cos(angle)
  local bulletDy = bulletSpeed * math.sin(angle)

  return bulletDx, bulletDy
end
Maybe it has something to do with the math.cos or math.sin, I honestly don't fully understand the math.

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 10:05 pm
by zorg
You should really store the data for individual bullets in separate table entries though... as with everything else, otherwise it will be infinitely more harder than it needs to be.

Code: Select all

function CalculateDxDy(x, y, startX, startY)
  local mouseX = x
  local mouseY = y

  -- the "base" angle for a shot
  local angle = math.atan2((mouseY - startY), (mouseX - startX))

  -- modify the angle based on other parameters
  -- minSpread should be greater or equal to 0, and less or equal to maxSpread.
  angle = angle + love.math.random(minSpread, maxSpread * 2.0) - maxSpread -- spread both clockwise and counterclockwise.

  local bulletDx = bulletSpeed * math.cos(angle)
  local bulletDy = bulletSpeed * math.sin(angle)

  return bulletDx, bulletDy
end
The issue with the above code is that you generally only want to calculate that random number once, when you spawn a projectile, not each time you update it, otherwise it will do weird things... not that that can't be an interesting mechanic. :P

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 10:59 pm
by pikuchan
Thanks a ton, I'll try that out and see how it works. Looks like I was probably just way overthinking it :)

Regarding your comment on the tables, can you elaborate a little bit as to what I am doing wrong? I've done professional programming for a LOOOONG time and tend to use tables like I would use a custom class. I'm always up for learning more proper ways of doing things.

Thanks again!

Re: Need help with shotgun blast logic

Posted: Sat May 27, 2017 11:07 pm
by zorg
Actually, now that i look at the above function, it's just that i'm a bit too used to seeing methods instead of "global" functions like that, called with minimal parameters. (Like, each entity, player or mook, having their own functions; whereas that calculating one could basically be re-used; both have their upsides and downsides... though OOP-like design with metatables would work too, since the code wouldn't be duplicated, yet all similar objects could use the same function, while it wouldn't hang around as a "global".)

And to be completely honest, i just noticed the table.insert in your createNewBullets function too... so there goes my foot in my mouth. :P

In any case, here's another part of your code altered a tiny bit, to support the fragment count part of my idea:

Code: Select all

if mouseDownTime >= rateOfFire then
      XofGun = player.x + (14 * math.cos(player.direction)) - ( 5 * math.sin(player.direction))
      YofGun = player.y + ( 5 * math.cos(player.direction)) + (14 * math.sin(player.direction))

      local x = love.mouse.getX()
      local y = love.mouse.getY()

      -- This works, since the limiting value will only be calculated once.
      for fragments =1, love.math.random(minFragmentCount, maxFragmentCount) do

          -- Then in CreateNewBullet, add in the angle parameter i mentioned above, that way it will be created on spawn, and not be randomized each update.
          CreateNewBullet(x, y, XofGun, YofGGun)

      end

      mouseDownTime = 0
    end

Re: Need help with shotgun blast logic

Posted: Sun May 28, 2017 12:18 am
by pikuchan
haha, no worries at all. I just figured there's a good chance I was doing something goofy so I was just taking the opportunity to learn from it. Playing around with that code you provided and I don't have it perfect yet, but getting some really cool effects just by tweaking the values some. Thanks for all the help.

Re: Need help with shotgun blast logic

Posted: Sun May 28, 2017 5:25 am
by ivan
The code might work, but it's not super efficient or clear.
Here are my suggestions:

Code: Select all

-- check if we are allowed to fire
if cooldown > 0 then
  cooldown = cooldown - dt
  return
end

-- angle
local a = player.direction
-- speed
local s = 100
-- offset
local xo, yo = 14, 5
local x = player.x + math.cos(a)*xo
local y = player.y + math.sin(a)*yo

-- adjust cooldown
cooldown = rateoffire

-- spawn new bullet(s) here
CreateNewBullet(x, y, a, s)
You don't need to store the bounding boxes:

Code: Select all

function CreateNewBullet(x, y, a, s, r)
  local dx = math.cos(a)*s
  local dy = math.sin(a)*s
  table.insert(bullets, {x = x, y = y, dx = dx, dy = dy, radius = r or 5 })
end
For the bullet update function, you are creating a new table each frame which is not ideal:

Code: Select all

function BulletUpdate(dt)
  for i=#bullets, 1, -1 do
    local b = bullets[I]
    b.x = b.x + b.dx*dt
    b.y = b.y + b.dy*dt

    -- did we hit something?
    if QueryMapAABB(b.x, b.y, b.radius, b.radius) == true then
      table.remove(bullets, i)
    end
  end
end