Page 1 of 1

Making Sprites Disappear

Posted: Sat Aug 22, 2020 8:47 pm
by Tomatotumtum
Hello,

New to Lua and Love2D here! I'm trying to finish a final project for this online course I'm taking, and I want to know how I could make a sprite disappear. Currently, I have it so that the player is in one class, and anything to do with the map (so tiles and background) in another. I want to write so that if the player hits a specific tile, then that tile disappears, and a counter goes up. I know that there was a similar question about this regarding the cs50 gamedev course with balls, but I didn't understand much of the code, so I'm hoping someone could help me. My

Code: Select all

map.lua
looks like this:

Code: Select all

require 'Util'
require 'Player'

Map = Class{}

-- Storing where all the sprites are in the tile spritesheet
EMPTY = 1
LID = 2
SUSHI1 = 3
SUSHI2 = 4

-- Storing where the background should be
BACKGROUND = 1

-- Scroll speed for the camera
local SCROLL_SPEED = 200

function Map:init()

    -- Storing the spritesheet
    self.background = love.graphics.newImage('Graphics/platform/NEW/back2.png')
    self.spritesheet = love.graphics.newImage('Graphics/platform/newplatforms/all.png')

    -- Storing the width and height of each sprite AND background width and height
    self.backgroundWidth = 500
    self.backgroundHeight = 2000
    self.tileWidth = 35
    self.tileHeight = 15

    -- Creating a player object so that it has access to the map properties
    -- Passing in the self object for that specific reason
    self.player = Player(self)

    -- Table to contain all the tile sprites
    self.tiles = {}
    -- Table to contain all the background sprites
    self.back = {}

    -- Stores the map width and height
    -- NOTE that these values are chosen through experimentation
    self.mapWidth = 15
    self.mapHeight = 133

    -- Cutting the sprites of the sheet AND background
    self.backgroundSprites = generateQuads(self.background, self.backgroundWidth, self.backgroundHeight)
    self.tileSprites = generateQuads(self.spritesheet, self.tileWidth, self.tileHeight)

    -- Setting the camera values
    self.camX = 0
    self.camY = self.backgroundHeight - VIRTUAL_HEIGHT

    -- Setting the background tiles
    for y = 1, self.mapHeight do
        for x = 1, self.mapWidth do
            self:setTileBackground(x, y, BACKGROUND)
        end
    end

    -- Setting all the tiles initially to be empty
    for y = 1, self.mapHeight do
        for x = 1, self.mapWidth do
            self:setTile(x, y, EMPTY)
        end
    end

    -- Setting the initial floor
    for x = 1, self.mapWidth do
        self:setTile(x, self.mapHeight, LID)
    end

    ------------------------------------------------- START OF MAP CREATION -------------------------------------------------
    local y = 1
    -- variable to help with the twist
    local location = 5
    while y < self.mapHeight do
        -- Make sure we're at least 6 tiles away from the bottom
        -- START OF EVERYTHING
        if y < self.mapHeight - 1 and y > 3 then
            -- FOR THE FIRST QUARTER OF THE MAP, LIDS COVER HALF WIDTH
            if y > self.mapHeight * 2/3 then
                
                for counter = 1, self.mapWidth / 2.5 do

                    -- As long as location isn't max, keep incrementing
                    if location < self.mapWidth then
                        location = location + 1
                    -- If location goes to max, then reset
                    else
                        location = 1
                    end
                 
                    -- Set the tile in the appropriate location
                    self:setTile(location, y, LID)
                end

            -- FOR THE SECOND QUARTER OF THE MAP, HARDER
            elseif y < self.mapHeight * 2/3 and y > self.mapHeight * 1/3 then
                
                for counter = 1, self.mapWidth / 2.5 do

                    -- As long as location isn't max, keep incrementing
                    if location < self.mapWidth then
                        location = location + 1
                    -- If location goes to max, then reset
                    else
                        location = 1
                    end
                 
                    -- Set the tile in the appropriate location
                    -- CHANGING BETWEEN SUSHI COVERS
                    if (counter % 2 == 0) then
                        self:setTile(location, y, SUSHI1)
                    else
                        self:setTile(location, y, SUSHI2)
                    end
                
                end
                
            -- FOR THE LAST QUARTER OF THE MAP
            else
                for counter = 1, self.mapWidth / 2.5 do

                    -- As long as location isn't max, keep incrementing
                    if location < self.mapWidth then
                        location = location + 1
                    -- If location goes to max, then reset
                    else
                        location = 1
                    end
                 
                    -- Set the tile in the appropriate location
                    -- CHANGING BETWEEN SUSHI COVERS
                    if (counter % 2 == 0) then
                        self:setTile(location, y, SUSHI1)
                    else
                        self:setTile(location, y, SUSHI2)
                    end

                end
            end
        end
        -- INCREMENTING SCANLINE
        y = y + 4
    end

end

-- Function to 'Set' what the background tiles should be in the map
function Map:setTileBackground(x, y, tile)
    self.back[(y - 1) * self.mapWidth + x] = tile
end

-- Function to 'Set' what the tiles should be in the map
function Map:setTile(x, y, id)
    self.tiles[(y - 1) * self.mapWidth + x] = id
end

-- Function to get the ID of the tile at a certain location
-- This passes the map in as pixel coordinates rather than tile IDs
-- The x and y here are pixel values, getting the tile at that pixel location
-- Note that getTile gets the tile as a TILE, and so needed adjustment
-- The + 1 is because pixel values would make it go back to a pixel based system, and that's
-- 0 indexed. Instead, we want it to be 1 indexed
function Map:tileAt(x, y)
    return {
        x = math.floor(x / self.tileWidth) + 1,
        y = math.floor(y / self.tileHeight) + 1,
        id = self:getTile(math.floor(x / self.tileWidth) + 1, math.floor(y / self.tileHeight) + 1)
    }
end

-- Function to figure out what background tile should be in the map
function Map:getTileBackground(x, y)
    return self.back[(y - 1) * self.mapWidth + x]
end

-- Function to figure out what tile should be in the map
function Map:getTile(x, y)
    return self.tiles[(y - 1) * self.mapWidth + x]
end

-- Function to define what the collidables are
function Map:collides(tile)
    -- Define the collidable tiles. It contains the
    -- constants of the tiles that are considered solid
    local collidables = {
        LID, SUSHI1, SUSHI2
    }

    -- This is the function that will be used to check if the tile id that's under us matches 
    --the collidables. It's essentially saying for every key/value pair for ipairs which is 
    -- iterating through all the k/v pairs, and the _ just means index. We aren't using the
    -- key here so it doesn't matter anyway
    for _, v in ipairs(collidables) do
        if tile.id == v then
            return true
        end
    end

    -- Return false if it isn't a collidable
    return false
end

function Map:update(dt)
    -- Updating the player
    self.player:update(dt)

    -- keep camera's coordinate following the player, preventing camera from
    -- scrolling past 0 to the left and the map's width
    self.camX = math.max(0, math.min(self.player.x - VIRTUAL_WIDTH / 2,
        math.min(self.backgroundWidth - VIRTUAL_WIDTH, self.player.x)))

    self.camY = math.max(0, math.min(self.player.y - VIRTUAL_HEIGHT * 0.815, 
        math.max(self.backgroundHeight - VIRTUAL_HEIGHT, self.player.y)))
end

function Map:render()
    for y = 1, self.mapHeight do
        for x = 1, self.mapWidth do
            love.graphics.draw(self.background, self.backgroundSprites[self:getTileBackground(x, y)],
                (x - 1) * self.backgroundWidth, (y - 1) * self.backgroundHeight)
            love.graphics.draw(self.spritesheet, self.tileSprites[self:getTile(x, y)],
                (x - 1) * self.tileWidth, (y - 1) * self.tileHeight)
        end
    end

    self.player:render()
end
My

Code: Select all

player.lua
file looks like this:

Code: Select all

Player = Class{}

require 'Animation'

-- Know where everything is in the sheet
STEVEN_JUMP = 1
STEVEN_IDLE = 2
STEVEN_WALK1 = 3
STEVEN_WALK2 = 4

-- Setting the movement speed
local MOVE_SPEED = 100

-- Jump velocity, gravity velocity
local JUMP_VELOCITY = 700
local GRAVITY = 40


function Player:init(map)

    -- Initializing the x and y coordinates of the character
    -- NOTE that for the y to be at the bottom, we must subtract the 30 of the platform, and 30
    -- of Steven himself
    self.x = map.tileWidth * 5 -- Essentially 300 pixels to the right
    self.y = map.backgroundHeight - 45

    -- Storing the width and height of the spritesheet
    self.width = 32
    self.height = 30

    -- The dx and dy variables
    self.dx = 0
    self.dy = 0

    -- Easier to type this way
    self.map = map

    -- Storing the spritesheet for Steven
    self.playerSheet = love.graphics.newImage('Graphics/platform/steven/all.png')

    -- Splicing up the spritesheet
    self.frames = generateQuads(self.playerSheet, self.width, self.height)

    -- INITIAL STATE
    self.state = 'idle'

    -- DIRECTION THE CHARACTER IS FACING, DEFAULT
    self.direction = 'right'

    --Animation table
    -- Each takes as an argument the parameters that we used in the Animation
    -- function in the next page, and so it needs textures, frames, etc 
    self.animations = {
        -- IDLE ANIMATION TABLE
        -- Notice that the {} is meant to be an argument into the Animation function,
        -- or class, but since it's only a table, we don't need to put ({}) but can just
        -- put {}
        ['idle'] = Animation {
            -- The texture that we'll use
            texture = self.texture,
            -- The frames that we're going to use
            frames = {
                self.frames[2]
            },
            -- Interval between frames
            interval = 1
        },

        ['walking'] = Animation {
            texture = self.texture,
            frames = {
                self.frames[3], self.frames[4]
            },
            interval = 0.15
        },

        ['jumping'] = Animation {
            texture = self.texture,
            frames = {
                self.frames[1]
            },
            interval = 1
        }
    }

    -- What the current animation is
    self.animation = self.animations['idle']
    self.currentFrame = self.animation:getCurrentFrame()

    -- Checking the states, returns a function as keys
    self.behaviors = {
        ['idle'] = function(dt)
            -- JUMPING STEVEN
            -- Notice how we need to create a wasPressed function as it's a one-time press
            if love.keyboard.wasPressed('space') then
                --  Set the velocity upwards so negative
                self.dy = -JUMP_VELOCITY
                -- Change the state
                self.state = 'jumping'
                -- Change the animation
                self.animation = self.animations['jumping']
            -- STEVEN MOVES
            elseif love.keyboard.isDown('a') then
                -- Change the direction
                self.direction = 'left'
                -- left movement
                self.dx = -MOVE_SPEED
                -- Change the state
                self.state = 'walking'
                -- Resetting the animation
                self.animations['walking']:restart()
                -- Change the animation
                self.animation = self.animations['walking']
            elseif love.keyboard.isDown('d') then
                -- Change the direction
                self.direction = 'right'
                -- right movement
                self.dx = MOVE_SPEED
                -- Change the state
                self.state = 'walking'
                -- Reset the animation
                self.animations['walking']:restart()
                -- Change the animation
                self.animation = self.animations['walking']
            else
                -- If we aren't pressing a or d, make him idle
                self.dx = 0
            end
        end,
        ['walking'] = function(dt)
            if love.keyboard.wasPressed('space') then
                --  Set the velocity upwards so negative
                self.dy = -JUMP_VELOCITY
                self.state = 'jumping'
                -- Change the animation
                self.animation = self.animations['jumping']
            -- STEVEN MOVES
            elseif love.keyboard.isDown('a') then
                -- Change the direction
                self.direction = 'left'
                -- left movement
                self.dx = -MOVE_SPEED
                -- Change the animation
                self.animation = self.animations['walking']
            elseif love.keyboard.isDown('d') then
                -- Change the direction
                self.direction = 'right'
                -- right movement
                -- Notice we add 80 to account for that weird slow-mo here
                self.dx = MOVE_SPEED
                -- Change the animation
                self.animation = self.animations['walking']
            else
                -- If we aren't pressing a or d, make him idle
                self.dx = 0
                self.state = 'idle'
                self.animation = self.animations['idle']
            end

            -- check if there's a tile directly beneath us
            if not self.map:collides(self.map:tileAt(self.x, self.y + self.height)) and
                not self.map:collides(self.map:tileAt(self.x + self.width - 1, self.y + self.height)) then
                
                -- if so, reset velocity and position and change state
                self.state = 'jumping'
                self.animation = self.animations['jumping']
            end
        end,
        ['jumping'] = function(dt)

            ------------------------------------------- TO DO IF PLAYER GOES UNDER CAMERA BOTTOM ------------------------------------------------------------
            -- break if we go below the surface
            if self.y > self.map.backgroundHeight then

            end

            -- This is to change and manuever in the air
            if love.keyboard.isDown('a') then
                self.direction = 'left'
                -- Change the moving velocity
                self.dx = -MOVE_SPEED
            elseif love.keyboard.isDown('d') then
                self.direction = 'right'
                -- Change the moving velocity
                self.dx = MOVE_SPEED
            end
            -- Setting the y velocity
            self.dy = self.dy + GRAVITY

            -- check if there's a tile directly beneath us
            if self.map:collides(self.map:tileAt(self.x, self.y + self.height)) or
                self.map:collides(self.map:tileAt(self.x + self.width - 1, self.y + self.height)) and self.dy < 0 then

                -- if so, reset velocity and position and change state
                self.dy = 0
                self.state = 'idle'
                self.animation = self.animations['idle']
                self.y = (self.map:tileAt(self.x, self.y + self.height).y - 1) * self.map.tileHeight - self.height
            end
        end
    }

end

function Player:update(dt)
    -- Calling the behaviors as a function
    self.behaviors[self.state](dt)

    --Updating the animation
    self.animation:update(dt)

    -- Get the current frame
    self.currentFrame = self.animation:getCurrentFrame()

    -- Changing the velocities
    self.x = self.x + self.dx * dt
    self.y = self.y + self.dy * dt
end

function Player:render()
    
    -- NOTE the X scaling factor, which is -1 as in to flip if it's right
    local scaleX
    if self.direction == 'right' then
        scaleX = 1
    else
        scaleX = -1
    end

    -- Drawing the character initially
    -- NOTE the extra arguments to change the rotation and OFFSET/FLIP
    -- 0 is the initial rotation, which we don't want
    -- Scaling by -1 changes it to flipping to their right corner, not left, and so
    -- we need to move the origin point (which is initially the top left) to the center
    -- as we want it to change place wrt the origin
    -- The 1 is scaling 1 by Y
    -- The last 2 arguments are adding the offset of whatever its width and height are
    -- ALSO need to account for the origin (since it draws it from the origin) to make
    -- sure Steven isn't floating
    love.graphics.draw(self.playerSheet, self.currentFrame, math.floor(self.x + self.width / 2), math.floor(self.y + self.height / 2),
        0, scaleX, 1,
        self.width / 2, self.height / 2)

end
I want to make it so that if the player jumps on a

Code: Select all

SUSHI2
block, for example, it would disappear.

Sorry for the long post. I really do want to put my best into this, and I'm trying to add as much as a normal game would, but I'm new to gamelogic and I'm not sure how to "remove" a tile from the screen. I'm asking this mostly as a way to later on implement how to remove an entire enemy upon its death, so that it would have some sort of health bar and when it reaches 0 the character would not be on the screen anymore, as I'm assuming it would be the same logic. Or, once some bullets hit an enemy they disappear off the screen. In any case, any help regarding this is appreciated! (I attached a picture of what the game looks like right now and which tile I mean, if that helps)

Re: Making Sprites Disappear

Posted: Tue Aug 25, 2020 11:08 pm
by JohnpaulTH
Hi Tomatotumtum,
First, it would be helpful if you could include the .love file, (see Game_Distribution ) or at least the main file with love.draw() in it.
Second, how I would make a tile disappear would be to set it to nil. If you want to reuse tiles, it might be worth creating an

Code: Select all

INVISIBLE
"ID". or something like that.
There is a lot of talent on this forum, and you will probably get a better answer, this is my best shot without a .love file.