[help] with rotating multiple randomized sprites using a table

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
nice
Party member
Posts: 191
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

[help] with rotating multiple randomized sprites using a table

Post by nice »

Hello everyone!

It's been a while since I last was on this forum and I've recently got back to scripting with Löve/Lua and I'm currently attempting to recreate a game I did 5 years ago with Löve2D as part of relearning Lua and make the game playable again.

A little backstory
So for this project, I've decided to separate a script that was 500 lines of code into several different scripts, thus making it a little bit easier for me to go through and read the code. So I've hit a bit of a roadblock with the asteroids themselves, what I have right now are a bunch of diamonds and triangles flying about in my "game". What's also supposed to happen when they fly around is that they should also slowly rotate as well and this is where I've got stuck.

It also doesn't really help that I made this game 5 years ago and I don't really exactly remember how everything works in the code but for me, I want to challenge myself by recreating the first game I did 5 years ago.

Below I've also added my "main.lua" in case that it helps with my problem.
Happy holidays everyone and take care of yourselves!

P.S. Sorry if I'm a bit rusty with the post's topic but that's what I'm sure how I would describe the problem

asteroid.lua, the relevant code

Code: Select all

player = require "player"
asteroidImage16 = love.graphics.newImage("asteroid16.png")
asteroidImage32 = love.graphics.newImage("asteroid32.png")

explosion = love.audio.newSource("ExplosionTest.wav", "static")

numAsteroids = 20

asteroids = {}

-- ASTEROD SPAWNER --
for i = 1, numAsteroids do
  local randomAsteroid = math.random(1,2)
  asteroids[i] = {}
  if randomAsteroid == 1 then
    asteroids[i].Width = 16
    asteroids[i].Height = 16
    asteroids[i].sprite = asteroidImage16
    asteroids[i].Velocity = 2
    asteroids[i].Angle = 0

  elseif randomAsteroid == 2 then
    asteroids[i].Width = 32
    asteroids[i].Height = 32
    asteroids[i].sprite = asteroidImage32
    asteroids[i].Velocity = 1
    asteroids[i].Angle = 0

  end

  rand = math.random(1,4)
  if rand == 1 then
    asteroids[i].posX = math.random(-400, -64)
    asteroids[i].posY = math.random(0, 600)
    asteroids[i].theta = -0.8 + (math.random() * 1.57)
  elseif rand == 2 then
    asteroids[i].posX = math.random(864, 2000)
    asteroids[i].posY = math.random(0, 600)
    asteroids[i].theta = 2.35 + (math.random() * 1.57)
  elseif rand == 3 then
    asteroids[i].posX = math.random(0, 800)
    asteroids[i].posY = math.random(-400, -64)
    asteroids[i].theta = 0.398 + (math.random() * 1.57)
  else
    asteroids[i].posX = math.random(0, 800)
    asteroids[i].posY = math.random(700, 2000)
    asteroids[i].theta = 4.71 + (math.random() * 1.57)
  end
end

function asteroids:update(dt)
  for i = 1, numAsteroids do

    asteroids[i].posX = asteroids[i].posX + math.cos(asteroids[i].theta) * asteroids[i].Velocity
    asteroids[i].posY = asteroids[i].posY + math.sin(asteroids[i].theta) * asteroids[i].Velocity

    if self[i].posX > 900 or self[i].posY > 700 or self[i].posX < -100 or self[i].posY < -100 then
      rand = math.random(1, 4)
      if rand == 1 then
        self[i].posX = math.random(-100, -64)
        self[i].posY = math.random(0, 600)
        self[i].theta = -0.785 + (math.random() * 1.57)
      elseif rand == 2 then
        self[i].posX = math.random(864, 900)
        self[i].posY = math.random(0, 600)
        self[i].theta = 2.35 + (math.random() * 1.57)
      elseif rand == 3 then
        self[i].posX = math.random(0, 800)
        self[i].posY = math.random(-100, -64)
        self[i].theta = 0.398 + (math.random() * 1.57)
      else
        self[i].posX = math.random(0, 800)
        self[i].posY = math.random(700, 900)
        self[i].theta = 4.71 + (math.random() * 1.57)
      end
    end
  end

  for i = 1, numAsteroids do
  -- Player TO ASTEROID COLLISION HERE --
    -- test each asteroid against Player
    if Player.posX > self[i].posX and Player.posX < self[i].posX + self[i].Width and Player.posY > self[i].posY and Player.posY < self[i].posY + self[i].Height then
      asteroids[i].posX = math.random(-100, -64)
      asteroids[i].posY = math.random(0, 600)
      asteroids[i].theta = -0.785 + (math.random() * 1.57 )

      -- NoteToSelf: Consider moving "Player.Lives" to "player.lua" or "main.lua" instead
      Player.Lives = Player.Lives - 1

      if Player.Lives == 0 then
        love.audio.stop(explosion)
        love.audio.play(explosion)
        Player.posX = 400
        Player.posY = 300
        love.load()
      end
    end
  -- NoteToSelf: TEST ASTEROID COLLISION AGAINST EVERY OBJECT IN TRAIL --
end
  -- ASTEROID TO ASTEROID COLLISION HERE --
  local alreadyFlipped = {}
  for k = 1, numAsteroids do
    alreadyFlipped[k] = false
  end

  for i = 1, numAsteroids do
    local curAsteroid = asteroids[i]
    for j = 1, numAsteroids do
      local isUpperLeftColliding = false
      local isUpperRightColliding = false
      local isLowerLeftColliding = false
      local isLowerRightColliding = false

      isUpperLeftColliding = curAsteroid.posX > asteroids[j].posX and
      curAsteroid.posX < asteroids[j].posX + asteroids[j].Width and
      curAsteroid.posY > asteroids[j].posY and
      curAsteroid.posY < asteroids[j].posY + asteroids[j].Height

      isUpperRightColliding = curAsteroid.posX + curAsteroid.Width > asteroids[j].posX and
      curAsteroid.posX + curAsteroid.Width < asteroids[j].posX + asteroids[j].Width and
      curAsteroid.posY > asteroids[j].posY and
      curAsteroid.posY < asteroids[j].posY + asteroids[j].Height

      isLowerLeftColliding = curAsteroid.posX > asteroids[j].posX and
      curAsteroid.posX < asteroids[j].posX + asteroids[j].Width and
      curAsteroid.posY + curAsteroid.Height > asteroids[j].posY and
      curAsteroid.posY + curAsteroid.Height < asteroids[j].posY + asteroids[j].Height

      isLowerRightColliding = curAsteroid.posX + curAsteroid.Width > asteroids[j].posX and
      curAsteroid.posX + curAsteroid.Width < asteroids[j].posX + asteroids[j].Width and
      curAsteroid.posY + curAsteroid.Height > asteroids[j].posY and
      curAsteroid.posY + curAsteroid.Height < asteroids[j].posY + asteroids[j].Height

    local isCurOnscreen = curAsteroid.posX > 0 and curAsteroid.posX < 800 and curAsteroid.posY > 0 and curAsteroid.posY < 600
    local isOtherOnscreen = asteroids[j].posX > 0 and asteroids[j].posX < 800 and asteroids[j].posY > 0 and asteroids[j].posY < 600

    if isCurOnscreen and isOtherOnscreen then
      if isUpperLeftColliding or isUpperRightColliding or
         isLowerLeftColliding or isLowerRightColliding then
        if alreadyFlipped[i] == false then
          curAsteroid.theta = curAsteroid.theta + math.pi
          alreadyFlipped[i] = true
        end
        if alreadyFlipped[j] == false then
          asteroids[j].theta = self[j].theta + math.pi
          alreadyFlipped[j] = true
        end
      end
    end
  end
end
end

function asteroids:draw()
  for i = 1, numAsteroids do

    love.graphics.draw(asteroids[i].sprite,
                       asteroids[i].posX,
                       asteroids[i].posY)
  end
end

return asteroid
main.lua

Code: Select all

player = require "player"
asteroid = require "asteroid"

function love.load(arg)
end

function love.update(dt)
  Player:update(dt)
  asteroids:update(dt)
end

function love.draw()
  love.graphics.setColor(1, 1, 1, 1)
  Player:draw()
  asteroids:draw()
end

function love.keypressed(key)
  Player:keypressed(key)
  if key ==  'escape' then
    love.event.quit()
  end
end

function love.keyreleased(key)
  Player:keyreleased(key)
end
player.lua

Code: Select all

local isLeftHeld
local isRightHeld
local isUpHeld
local maxVelocity

isUpHeld = false
isLeftHeld = false
isRightHeld = false

Player = {}
Player.posX = 400
Player.posY = 300
Player.theta = 4.71
Player.Width = 32
Player.Height = 32
Player.Velocity = 0
Player.sprite = love.graphics.newImage("sprite.png")
Player.Lives = 3
maxVelocity = 5.25

function Player:update(dt)
  local pos = {}
  pos.x = self.posX
  pos.y = self.posY
  pos.theta = self.theta

  if isUpHeld == true then
    self.Velocity = self.Velocity + 0.5
  else
    self.Velocity = self.Velocity - 0.05
  end

  if isLeftHeld == true then
    self.theta = self.theta - 0.1
  end

  if isRightHeld == true then
    self.theta = self.theta + 0.1
  end

  if self.Velocity < 0 then
    self.Velocity = 0
  end

  if self.Velocity > maxVelocity then
    self.Velocity = maxVelocity
  end

  self.posX = self.posX + math.cos(self.theta) * self.Velocity
  self.posY = self.posY + math.sin(self.theta) * self.Velocity

  -- WRAP AROUND CODE --
    if self.posX > 832 then
      self.posX = -32
    elseif self.posX < -32 then
      self.posX = 832
    elseif self.posY > 632 then
      self.posY = -32
    elseif self.posY < -32 then
      self.posY = 632
    end

end

function Player:draw()
  love.graphics.draw(self.sprite, self.posX, self.posY,
                     self.theta, 1, 1, 16, 16)
end

function Player:keypressed(key)
  if key == 'up' then
    isUpHeld = true
  end

  if key == 'left' then
    self.theta = self.theta - 0.1
  end
  if key == 'right' then
    self.theta = self.theta + 0.1
  end

  if key == 'left' then
    isLeftHeld = true
  end
  if key == 'right' then
    isRightHeld = true
  end

end

function Player:keyreleased(key)
  if key == 'up' then
    isUpHeld = false
  end

  if key == 'left' then
    isLeftHeld = false
  end
  if key == 'right' then
    isRightHeld = false
  end
end

return player
:awesome: Have a good day! :ultraglee:
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: [help] with rotating multiple randomized sprites using a table

Post by pgimeno »

If all you want is visual rotation, you can use the rotation parameter in love.graphics.draw. You will also need to use the offset parameter to control the rotation centre. You probably want it at half the image's width and height but that's not something I can tell for sure without seeing the images.

The idea would be to store an angle per asteroid (so that different asteroids can be at different angles). Maybe also an angular velocity per asteroid (so they can also rotate at different speeds). Every step, you would increase the angle by dt times the angular velocity. Even better, after increasing it, keep only the modulo of dividing the angle by (2*math.pi) so that it doesn't grow too much, but that's probably unnecessary.

Collision with the correct visual shape is harder. If you want that, maybe you can use HC or love.physics. In some cases, maybe you can also handle pixel collision. I've done that for T2R.
User avatar
nice
Party member
Posts: 191
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

Re: [help] with rotating multiple randomized sprites using a table

Post by nice »

pgimeno wrote: Fri Dec 21, 2018 4:07 pm If all you want is visual rotation, you can use the rotation parameter in love.graphics.draw. You will also need to use the offset parameter to control the rotation centre. You probably want it at half the image's width and height but that's not something I can tell for sure without seeing the images.

The idea would be to store an angle per asteroid (so that different asteroids can be at different angles). Maybe also an angular velocity per asteroid (so they can also rotate at different speeds). Every step, you would increase the angle by dt times the angular velocity. Even better, after increasing it, keep only the modulo of dividing the angle by (2*math.pi) so that it doesn't grow too much, but that's probably unnecessary.

Collision with the correct visual shape is harder. If you want that, maybe you can use HC or love.physics. In some cases, maybe you can also handle pixel collision. I've done that for T2R.
I've figured it would be something with the rotation parameter, I've tried it before but every time I did the rotation was all wonky as hell (like my "asteroids" had a life of their own). But I will go back again later to it and attempt what you've instructed, maybe there's something I've missed in my furstration
:awesome: Have a good day! :ultraglee:
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: [help] with rotating multiple randomized sprites using a table

Post by pgimeno »

The most important thing is to get the centre right. Mind you, changing that will also offset the position of your asteroids, so if you want it to work as a drop-in replacement for your draw function, you probably need to add the offset to the X and Y position.

Compare the results with rotating without proper centring:

Code: Select all

local img = love.graphics.newImage('add the path to an asteroid image here')
local angle = 0
local angularSpeed = 0.1

function love.update(dt)
  angle = angle + angularSpeed * dt
end

function love.draw()
  local scrWidth, scrHeight = love.graphics.getDimensions()
  local scrCentreX, scrCentreY = scrWidth/2, scrHeight/2

  love.graphics.draw(img, scrCentreX, scrCentreY, angle)
end
and with the image properly centred (assuming the asteroid's centre exactly matches the image's centre:

Code: Select all

local img = love.graphics.newImage('add the path to an asteroid image here')
local angle = 0
local angularSpeed = 0.1

function love.update(dt)
  angle = angle + angularSpeed * dt
end

function love.draw()
  local scrWidth, scrHeight = love.graphics.getDimensions()
  local scrCentreX, scrCentreY = scrWidth/2, scrHeight/2

  local imgWidth, imgHeight = img:getDimensions()
  local imgCentreX, imgCentreY = imgWidth / 2, imgHeight / 2

  love.graphics.draw(img, scrCentreX, scrCentreY, angle, 1, 1, imgCentreX, imgCentreY)
end
With that code, the coordinates you draw at are those of the centre of the image, which may be inconvenient for you. If you need to compensate in order to keep drawing at the top left corner, just add imgCentreX and imgCentreY to the draw coordinates. The love.graphics.draw call of the example above would then look like this:

Code: Select all

  love.graphics.draw(img, scrCentreX + imgCentreX, scrCentreY + imgCentreY, angle, 1, 1, imgCentreX, imgCentreY)
Post Reply

Who is online

Users browsing this forum: No registered users and 232 guests