I have created a basic chunk generator, a chunk is area filled with squares, when the player moves a few blocks away from 0,0 it works correctly but after moving 4 chunks away it renders more then one chunk instead of one, I am not sure what I am doing wrong, I have given it a go changing some values, but I am left head scratching.
here is the full code, you can copy and paste into VSCODE with love2D to see what happens.
I think the main issue is somewhere around check_boarders function since that is what checks if the player is inside a chunk.
function Key_input(key)
if love.keyboard.isDown(key) then
return 1
else
return 0
end
end
function love.load()
Camera = require "camera"
Cam = Camera()
Basic_Player = {}
Basic_Player.X = 100
Basic_Player.Y = 100
Basic_Player.Speed = 15
Movement = {}
end
function love.update(dt)
Movement.X = Key_input("d") - Key_input("a")
Movement.Y = Key_input("s") - Key_input("w")
Basic_Player.X = Basic_Player.X + Movement.X * Basic_Player.Speed
Basic_Player.Y = Basic_Player.Y + Movement.Y * Basic_Player.Speed
Cam:lookAt(Basic_Player.X,Basic_Player.Y)
X, Y = Cam:position() -- Set cam position to global values
end
function love.draw()
love.graphics.setBackgroundColor(0.5,0.5,0.9)
Cam:attach() -- Renders the player and world inside its own scene
generate_world(10,0)
love.graphics.setColor( 0,0,1, 1 )
love.graphics.rectangle("fill",Basic_Player.X,Basic_Player.Y,30,30)
love.graphics.setColor( 1,1,1, 1 )
Cam:detach()
love.graphics.setColor( 1,0,0, 1 ) --Stays on the screen
love.graphics.print(X .. " / " .. Y ,300,400)
love.graphics.print(love.timer.getFPS( ) ,300,450)
love.graphics.setColor( 1,1,1, 1 )
end
function old_generate_world(_world_size, _seed) -- Before optimization
local _chunk_size = 30
local _block_size = 30
for i = 0, _world_size - 1 do
for f = 0, _world_size - 1 do
local x_val = (_chunk_size * _block_size) * i -- Position value for actually building the chunks
local y_val = (_chunk_size * _block_size) * f
gen_chunk(_chunk_size,_block_size,_seed,{X = x_val ,Y = y_val })
end
end
end
function generate_world(_world_size, _seed)
local _chunk_size = 10 -- Chunk size width and height
local _block_size = 30 -- block size inside the chunk
for i = 0, _world_size - 1 do -- loop through world size
for f = 0, _world_size - 1 do
local x_val = (_chunk_size * _block_size) * i -- Position value for actually building the chunks
local y_val = (_chunk_size * _block_size) * f
local chunk_x_local_size = 0 -- To make sure we get a length for when i and f = 0
local chunk_y_local_size = 0
if i == 0 then -- To make sure the size of the chunk isnt zero
chunk_x_local_size = _chunk_size * _block_size -- Get length of chunk when i = 0
else
chunk_x_local_size = x_val
end
if f == 0 then -- ditto
chunk_y_local_size = _chunk_size * _block_size
else
chunk_y_local_size = y_val
end
-- Checks if the player is inside a chunk if true draw it.
if Check_boarders({X = X,Y = Y},{X = x_val,Y = y_val}, {X = chunk_x_local_size, Y = chunk_y_local_size}) then
gen_chunk(_chunk_size,_block_size,_seed,{X = x_val ,Y = y_val }) -- Actually generate the chunk
end
love.graphics.setColor( 0,1,0, 1 )
love.graphics.rectangle("fill",x_val,y_val,15,15)
love.graphics.setColor( 1,1,1, 1 )
end
end
end
function Check_boarders(player_pos, boarder_pos, chunk_length) -- Checks player position is inside the boarder of the currently generated chunk
if player_pos.X > boarder_pos.X and player_pos.X < boarder_pos.X + chunk_length.X then -- Check if the player is greater then top left and less then top right
if player_pos.Y > boarder_pos.Y and player_pos.Y < boarder_pos.Y + chunk_length.Y then -- check if player is greater then top and less then bottom left
return true
end
end
return false
end
function gen_chunk(chunk_size,block_size,seed,position) -- chunk size is how many blocks inside the chunk, block size is self explain, seed n/a, pos starting chunk position
for i = 0, chunk_size - 1 do
for e = 0, chunk_size - 1 do -- loop until chunk size is met this is the amount of blocks being created
love.graphics.rectangle("fill",position.X + i * block_size,position.Y + e * block_size,block_size - 1,block_size - 1)
end
end
love.graphics.setColor( 1,0,0, 1 )
love.graphics.rectangle("fill",position.X ,position.Y,6,6)
love.graphics.setColor( 1,1,1, 1 )
end
You will need this camera.lua script just create it and paste this into it:
local _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
local cos, sin = math.cos, math.sin
local camera = {}
camera.__index = camera
-- Movement interpolators (for camera locking/windowing)
camera.smooth = {}
function camera.smooth.none()
return function(dx,dy) return dx,dy end
end
function camera.smooth.linear(speed)
assert(type(speed) == "number", "Invalid parameter: speed = "..tostring(speed))
return function(dx,dy, s)
-- normalize direction
local d = math.sqrt(dx*dx+dy*dy)
local dts = math.min((s or speed) * love.timer.getDelta(), d) -- prevent overshooting the goal
if d > 0 then
dx,dy = dx/d, dy/d
end
return dx*dts, dy*dts
end
end
function camera.smooth.damped(stiffness)
assert(type(stiffness) == "number", "Invalid parameter: stiffness = "..tostring(stiffness))
return function(dx,dy, s)
local dts = love.timer.getDelta() * (s or stiffness)
return dx*dts, dy*dts
end
end
local function new(x,y, zoom, rot, smoother)
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
zoom = zoom or 1
rot = rot or 0
smoother = smoother or camera.smooth.none() -- for locking, see below
return setmetatable({x = x, y = y, scale = zoom, rot = rot, smoother = smoother}, camera)
end
function camera:lookAt(x,y)
self.x, self.y = x, y
return self
end
function camera:move(dx,dy)
self.x, self.y = self.x + dx, self.y + dy
return self
end
function camera:position()
return self.x, self.y
end
function camera:rotate(phi)
self.rot = self.rot + phi
return self
end
function camera:rotateTo(phi)
self.rot = phi
return self
end
function camera:zoom(mul)
self.scale = self.scale * mul
return self
end
function camera:zoomTo(zoom)
self.scale = zoom
return self
end
function camera:attach(x,y,w,h, noclip)
x,y = x or 0, y or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
self._sx,self._sy,self._sw,self._sh = love.graphics.getScissor()
if not noclip then
love.graphics.setScissor(x,y,w,h)
end
local cx,cy = x+w/2, y+h/2
love.graphics.push()
love.graphics.translate(cx, cy)
love.graphics.scale(self.scale)
love.graphics.rotate(self.rot)
love.graphics.translate(-self.x, -self.y)
end
function camera:detach()
love.graphics.pop()
love.graphics.setScissor(self._sx,self._sy,self._sw,self._sh)
end
function camera:draw(...)
local x,y,w,h,noclip,func
local nargs = select("#", ...)
if nargs == 1 then
func = ...
elseif nargs == 5 then
x,y,w,h,func = ...
elseif nargs == 6 then
x,y,w,h,noclip,func = ...
else
error("Invalid arguments to camera:draw()")
end
self:attach(x,y,w,h,noclip)
func()
self:detach()
end
-- world coordinates to camera coordinates
function camera:cameraCoords(x,y, ox,oy,w,h)
ox, oy = ox or 0, oy or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
local c,s = cos(self.rot), sin(self.rot)
x,y = x - self.x, y - self.y
x,y = c*x - s*y, s*x + c*y
return x*self.scale + w/2 + ox, y*self.scale + h/2 + oy
end
-- camera coordinates to world coordinates
function camera:worldCoords(x,y, ox,oy,w,h)
ox, oy = ox or 0, oy or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
local c,s = cos(-self.rot), sin(-self.rot)
x,y = (x - w/2 - ox) / self.scale, (y - h/2 - oy) / self.scale
x,y = c*x - s*y, s*x + c*y
return x+self.x, y+self.y
end
function camera:mousePosition(ox,oy,w,h)
local mx,my = love.mouse.getPosition()
return self:worldCoords(mx,my, ox,oy,w,h)
end
-- camera scrolling utilities
function camera:lockX(x, smoother, ...)
local dx, dy = (smoother or self.smoother)(x - self.x, self.y, ...)
self.x = self.x + dx
return self
end
function camera:lockY(y, smoother, ...)
local dx, dy = (smoother or self.smoother)(self.x, y - self.y, ...)
self.y = self.y + dy
return self
end
function camera:lockPosition(x,y, smoother, ...)
return self:move((smoother or self.smoother)(x - self.x, y - self.y, ...))
end
function camera:lockWindow(x, y, x_min, x_max, y_min, y_max, smoother, ...)
-- figure out displacement in camera coordinates
x,y = self:cameraCoords(x,y)
local dx, dy = 0,0
if x < x_min then
dx = x - x_min
elseif x > x_max then
dx = x - x_max
end
if y < y_min then
dy = y - y_min
elseif y > y_max then
dy = y - y_max
end
-- transform displacement to movement in world coordinates
local c,s = cos(-self.rot), sin(-self.rot)
dx,dy = (c*dx - s*dy) / self.scale, (s*dx + c*dy) / self.scale
-- move
self:move((smoother or self.smoother)(dx,dy,...))
end
-- the module
return setmetatable({new = new, smooth = camera.smooth},
{__call = function(_, ...) return new(...) end})
I am getting the message "This game indicates it was made for version '0.9.1' of LOVE.
It may not be compatible with the running version (0.10.2)." when I try and run my game. The game still works but the message is annoying me. How do I update it to the latest version? My code is here:
debug = true
Main.lua
-- Timers
-- We declare these here so we don't have to edit them multiple places
canShoot = true
canShootTimerMax = 0.2
canShootTimer = canShootTimerMax
createEnemyTimerMax = 0.4
createEnemyTimer = createEnemyTimerMax
-- Player Object
player = { x = 200, y = 710, speed = 150, img = nil }
isAlive = true
score = 0
-- Image Storage
bulletImg = nil
enemyImg = nil
-- Entity Storage
bullets = {} -- array of current bullets being drawn and updated
enemies = {} -- array of current enemies on screen
-- Collision detection taken function from http://love2d.org/wiki/BoundingBox.lua
-- Returns true if two boxes overlap, false if they don't
-- x1,y1 are the left-top coords of the first box, while w1,h1 are its width and height
-- x2,y2,w2 & h2 are the same, but for the second box
function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2)
return x1 < x2+w2 and
x2 < x1+w1 and
y1 < y2+h2 and
y2 < y1+h1
end
-- Loading
function love.load(arg)
player.img = love.graphics.newImage('assets/plane.png')
enemyImg = love.graphics.newImage('assets/enemy.png')
bulletImg = love.graphics.newImage('assets/bullet.png')
end
-- Updating
function love.update(dt)
-- I always start with an easy way to exit the game
if love.keyboard.isDown('escape') then
love.event.push('quit')
end
-- Time out how far apart our shots can be.
canShootTimer = canShootTimer - (1 * dt)
if canShootTimer < 0 then
canShoot = true
end
-- Time out enemy creation
createEnemyTimer = createEnemyTimer - (1 * dt)
if createEnemyTimer < 0 then
createEnemyTimer = createEnemyTimerMax
-- Create an enemy
randomNumber = math.random(10, love.graphics.getWidth() - 10)
newEnemy = { x = randomNumber, y = -10, img = enemyImg }
table.insert(enemies, newEnemy)
end
-- update the positions of bullets
for i, bullet in ipairs(bullets) do
bullet.y = bullet.y - (250 * dt)
if bullet.y < 0 then -- remove bullets when they pass off the screen
table.remove(bullets, i)
end
end
-- update the positions of enemies
for i, enemy in ipairs(enemies) do
enemy.y = enemy.y + (200 * dt)
if enemy.y > 850 then -- remove enemies when they pass off the screen
table.remove(enemies, i)
end
end
-- run our collision detection
-- Since there will be fewer enemies on screen than bullets we'll loop them first
-- Also, we need to see if the enemies hit our player
for i, enemy in ipairs(enemies) do
for j, bullet in ipairs(bullets) do
if CheckCollision(enemy.x, enemy.y, enemy.img:getWidth(), enemy.img:getHeight(), bullet.x, bullet.y, bullet.img:getWidth(), bullet.img:getHeight()) then
table.remove(bullets, j)
table.remove(enemies, i)
score = score + 1
end
end
if CheckCollision(enemy.x, enemy.y, enemy.img:getWidth(), enemy.img:getHeight(), player.x, player.y, player.img:getWidth(), player.img:getHeight())
and isAlive then
table.remove(enemies, i)
isAlive = false
end
end
if love.keyboard.isDown('left','a') then
if player.x > 0 then -- binds us to the map
player.x = player.x - (player.speed*dt)
end
elseif love.keyboard.isDown('right','d') then
if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
player.x = player.x + (player.speed*dt)
end
end
if love.keyboard.isDown(' ', 'rctrl', 'lctrl', 'ctrl') and canShoot then
-- Create some bullets
newBullet = { x = player.x + (player.img:getWidth()/2), y = player.y, img = bulletImg }
table.insert(bullets, newBullet)
canShoot = false
canShootTimer = canShootTimerMax
end
if not isAlive and love.keyboard.isDown('r') then
-- remove all our bullets and enemies from screen
bullets = {}
enemies = {}
-- reset timers
canShootTimer = canShootTimerMax
createEnemyTimer = createEnemyTimerMax
-- move player back to default position
player.x = 50
player.y = 710
-- reset our game state
score = 0
isAlive = true
end
end
-- Drawing
function love.draw(dt)
for i, bullet in ipairs(bullets) do
love.graphics.draw(bullet.img, bullet.x, bullet.y)
end
for i, enemy in ipairs(enemies) do
love.graphics.draw(enemy.img, enemy.x, enemy.y)
end
love.graphics.setColor(255, 255, 255)
love.graphics.print("SCORE: " .. tostring(score), 400, 10)
if isAlive then
love.graphics.draw(player.img, player.x, player.y)
else
love.graphics.print("Press 'R' to restart", love.graphics:getWidth()/2-50, love.graphics:getHeight()/2-10)
end
if debug then
fps = tostring(love.timer.getFPS())
love.graphics.print("Current FPS: "..fps, 9, 10)
end
end
conf.lua
-- Configuration
function love.conf(t)
t.title = "Scrolling Shooter Tutorial" -- The title of the window the game is in (string)
t.version = "0.9.1" -- The LÖVE version this game was made for (string)
t.window.width = 480 -- we want our game to be long and thin.
t.window.height = 800
-- For Windows debugging
t.console = true
end
As noted in the comments, the problem in your case was that, in conf.lua, the version was specified as "0.9.1". In some cases, changing this value to "0.10.2" is sufficient, but a significant amount of changes occurred between 0.9.0 and 0.10.0.
Be especially aware that mouse input is definitely going to be broken because, in versions before 0.10.0, LOVE used strings to represent mouse buttons, whereas in 0.10.0 and beyond, numbers are used. To fix this, look for mouse-related functions (love.mouse.isDown, love.mousepressed, etc.) and change "l" to 1, "r" to 2, and so on. See the full list of old values and love.mousepressed for more. Additionally, mousewheel movement changed as well, with the addition of the love.wheelmoved callback and removing the strings passed to love.mousepressed.
Additionally, read through the changelog for any changes that may have affected your program.
I'm totally new to Lua and Love2D and probably do not understand the concepts at all.
Well this is a Love2D tutorial, and I want to change it so when I press "a", for example, on the keyboard, the object will exchange (from hamster to car) and so on.
Can you help me out?
-- Tutorial 1: Hamster Ball
-- Add an image to the game and move it around using
-- the arrow keys.
-- compatible with löve 0.6.0 and up
function love.load()
hamster = love.graphics.newImage("hamster.png")
auto = love.graphics.newImage("auto.png")
x = 50
y = 50
speed = 300
end
function love.update(dt)
if love.keyboard.isDown("right") then
x = x + (speed * dt)
end
if love.keyboard.isDown("left") then
x = x - (speed * dt)
end
if love.keyboard.isDown("down") then
y = y + (speed * dt)
end
if love.keyboard.isDown("up") then
y = y - (speed * dt)
end
if love.keyboard.isDown("escape") then
love.event.quit()
end
if love.keyboard.isDown("a") then
love.draw(auto,x,y)
end
end
function love.draw()
love.graphics.draw(hamster, x, y)
end
Well thanks Corbin, i figured it out without "state" local variable. Your solution was inspiring to me. Now it's working.
-- Tutorial 1: Hamster Ball
-- Add an image to the game and move it around using
-- the arrow keys.
-- compatible with löve 0.6.0 and up
function love.load()
hamster = love.graphics.newImage("hamster.png")
auto = love.graphics.newImage("auto.png")
activeImage = hamster
activeImageName = "hamster"
x = 50
y = 50
speed = 300
end
function love.update(dt)
if love.keyboard.isDown("right") then
x = x + (speed * dt)
end
if love.keyboard.isDown("left") then
x = x - (speed * dt)
end
if love.keyboard.isDown("down") then
y = y + (speed * dt)
end
if love.keyboard.isDown("up") then
y = y - (speed * dt)
end
if love.keyboard.isDown("escape") then
love.event.quit()
end
if love.keyboard.isDown("a") then
activeImage = auto
activeImageName = "auto"
end
if love.keyboard.isDown("h") then
activeImage = hamster
activeImageName = "hamster"
end
end
function love.draw()
love.graphics.draw(activeImage, x, y)
end
I'd suggest using love.update to only update state. Don't draw in it. Then do all of your drawing in love.draw. A solution might be:
local state = {}
function love.load()
hamster = love.graphics.newImage("hamster.png")
auto = love.graphics.newImage("auto.png")
state.activeImage = hamster
state.activeImageName = "hamster"
-- <snip> ...
end
function love.update(dt)
-- <snip> ...
if love.keyboard.isDown("a") then
if state.activeImageName == "hamster" then
state.activeImage = auto
state.activeImageName = "auto"
else
state.activeImage = hamster
state.activeImageName = "hamster"
end
end
end
function love.draw()
love.graphics.draw(state.activeImage, x, y)
end
This is probably not the best way to solve this, but it works - create an object which has an X value, a Y value, a speed value, and an IMG value. Set the X and Y to 50 and 50, the speed to 300, and set the IMG to nil. In the load function, set the IMG value to whichever image you would prefer it to start as. In the update function, check for a key press and when the key is pressed change the IMG value to the new image.
An example script for clarification:
player = {X = 50, Y = 50, speed = 300, IMG = nil}
function love.load()
hamster = love.graphics.newImage('hamster.png')
auto = love.graphics.newImage('auto.png')
player.IMG = hamster
end
function love.update(dt)
if love.keyboard.isDown('d') then
player.x = player.x + player.speed
elseif love.keyboard.isDown('a') then
player.x = player.x - player.speed
end
if love.keyboard.isDown('w') then
player.y = player.y + player.speed
elseif love.keyboard.isDown('s') then
player.y = player.y = player.speed
end
if love.keyboard.isDown('space') then
if player.IMG == hamster then
player.IMG = auto
else
player.IMG = hamster
end
end
function love.draw()
love.graphics.draw(player.IMG, player.X, player.Y)
end
Hopefully this helps!