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
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
Code: Select all
player.lua
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
Code: Select all
SUSHI2
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)