[solved] sti/gamera woes

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
abghost
Prole
Posts: 2
Joined: Wed Jul 05, 2017 6:24 am

[solved] sti/gamera woes

Post by abghost »

Hey all,

I'm a grade-A novice at game programming, trying to learn good ecs coding practices while slowly getting my feet wet with some love/lua libraries. (And hi hello forum, you've seemed very pleasant from my lurking, and I'm really enjoying working with love.) Right now my goal is to recreate the functionality of the lua.space Tiled article with an ecs structure, using tiny-ecs, sti, and gamera. I'm taking a lot of cues (aka puzzling over/copying from) from the tiny-ecs demo code, making adjustments and simplifying where I can.

My problem is that, while the camera's position is clearly getting set, it isn't getting drawn, so camera position is moving but the map isn't getting translated. I'm sure the issue is something I overlooked, but I have pored over the Commando Kibbles code to see what I'm doing differently and am still scratching my head. I didn't include Player.lua and SpriteSystem.lua for simplicity's sake, but if you think they're relevant, I'll edit them in.

main.lua

Code: Select all

class = require("lib/middleclass")
tiny = require("lib/tiny")
local gamera = require("lib/gamera")
local sti = require("lib/sti")
local camera = nil

local tileMap = nil
local world = nil

function love.load()
	tileMap = sti("assets/badshore.lua")
	camera = gamera.new(0,0,tileMap.width*tileMap.tilewidth, tileMap.height*tileMap.tileheight)
	world = tiny.world(
		require("src.PlayerControlSystem")(tileMap),
		require("src.CameraTrackingSystem")(camera),
		require("src.TileMapRenderSystem")(tileMap, camera),
		require("src.SpriteSystem")(),
		require("src.Player")({x=200, y=200})
	)
end

function love.update(dt)
end

function love.draw()
	local dt = love.timer.getDelta()
	if world then
		world:update(dt)
	end
end
src.CameraTrackingSystem.lua

Code: Select all

local CameraTrackingSystem = tiny.processingSystem(class "CameraTrackingSystem")

CameraTrackingSystem.filter = tiny.requireAll("position")

function CameraTrackingSystem:initialize(camera)
	self.camera = camera
end

function CameraTrackingSystem:process(e, dt)
	local tx, ty = math.floor(e.position.x), math.floor(e.position.y)
	local camx, camy = self.camera:getPosition()
	local lerp = 0.1
	print("offset: "..tx..", "..ty.."; cam: "..camx..", "..camy)
	self.camera:setPosition(round(camx + (tx - camx) * lerp), round(camy + (ty - camy) * lerp))
end

return CameraTrackingSystem
src.TileMapRenderSystem.lua

Code: Select all

local TileMapRenderSystem = tiny.system(class "TileMapRenderSystem")

function TileMapRenderSystem:initialize(map, camera)
	self.camera = camera
	self.map = map

	function self.drawFn(l, t, w, h)
		map:update(dt)
		map:draw(-l, -t)
	end
end

function TileMapRenderSystem:update(dt)
	self.camera:draw(self.drawFn)
end

return TileMapRenderSystem
src.PlayerControlSystem.lua

Code: Select all

local PlayerControlSystem = tiny.processingSystem(class "PlayerControlSystem")

PlayerControlSystem.filter = tiny.requireAll("controllable", "position", "speed")

function PlayerControlSystem:initialize(tileMap)
	local layer = tileMap.layers["Sprites"]
	self.atStart = true
	self.start = {x=0, y=0}
	
	for k, object in pairs(tileMap.objects) do
		if object.name == "Player" then
			self.start.x = object.x
			self.start.y = object.y
			print("location: "..object.x..", "..object.y)
			break
		end
	end
end

function PlayerControlSystem:process(player, dt)
	if self.atStart then
		player.position = self.start
	end

	local dx, dy = 0, 0

	if love.keyboard.isDown("w", "a", "s", "d", "up", "down", "left", "right") then
		self.atStart = false
	end

	if love.keyboard.isDown("w", "up") then
		dy = dy - player.speed * dt
	end
	if love.keyboard.isDown("s", "down") then
		dy = dy + player.speed * dt
	end
	if love.keyboard.isDown("a", "left") then
		dx = dx - player.speed * dt
	end
	if love.keyboard.isDown("d", "right") then
		dx = dx + player.speed * dt
	end

	if dx ~= 0 and dy ~= 0 then
		dx = dx * 0.707106781
		dy = dy * 0.707106781
	end

	player.position.x = player.position.x + dx
	player.position.y = player.position.y + dy

end

return PlayerControlSystem
Edit: So the first problem was that map:draw wasn't using the l,t values from the camera, which I've now added. Now what I'm encountering is that the character starts moving 2x faster than the camera once it leaves the top-left quadrant of the screen. I'm guessing there's knowledge that I'm missing, but my best guess is that somehow the camera's new position is altering what (0,0) means for the character's position. Is there a common solution for this problem? Do I need to be rendering the character sprite inside an STI layer to maintain the map's coordinates under the changing camera?

I attached the code in case it's easier for answering questions.
Attachments
ecs-test.zip
(353.63 KiB) Downloaded 135 times
Last edited by abghost on Sun Jul 09, 2017 9:28 pm, edited 1 time in total.
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: sti/gamera woes

Post by bobbyjones »

I believe you want to draw your player in the cameras draw function.
abghost
Prole
Posts: 2
Joined: Wed Jul 05, 2017 6:24 am

Re: sti/gamera woes

Post by abghost »

You mean eliminate SpriteSystem entirely, and dump the sprite-drawing code in CameraTrackingSystem? I tried that, but it didn't change anything.

If I switch from using a camera library to using the code to translate the map from the lua.space article (uncomment a bunch of stuff in TileRenderSystem to make it a processingSystem instead of a system), the same problem happens (character moving 2x as fast as the camera), but I didn't have this problem in my pre-ecs version. So I'm really stumped about why the player seems to be obeying a different set of coordinates now than previously. :death:

EDIT: even made a gif of the behavior. To reiterate, after the player moves outside of the top left quadrant, its speed increases, and I can't figure out why. In the terminal window underneath, offset and cam are the player's position and the camera's position, as they report them.

Image

EDITx2: I slightly hoped this wasn't the problem, but turns out if you're using sti and you try and position sprites outside of a layer, they stop following world coordinates, instead (I think) following window coordinates as if they were world coordinates? I may have that backwards, but I think it's in the right direction. I had to shunt some layer:draw code into SpriteSystem, which felt weird and backwards, because now it looks like this:

Code: Select all

local SpriteSystem = tiny.processingSystem(class "SpriteSystem")

SpriteSystem.filter = tiny.requireAll("sprite", "position")

function SpriteSystem:initialize(map)
	self.map = map
	self.map:addCustomLayer("Sprites")
	self.spriteLayer = self.map.layers["Sprites"]
	self.spriteLayer.sprites = {}
	self.spriteLayer.drawReady = false
	
	function self.spriteLayer:draw()
		if self.sprites and self.drawReady then
			local n = self.sprites.thisguy
			local sprite, x, y, ox, oy = n.sprite, n.x, n.y, n.ox, n.oy
			-- local sprite, x, y, ox, oy = e.sprite, e.position.x, e.position.y, e.offset.x, e.offset.y
			love.graphics.draw(sprite, x, y, 0, 1, 1, ox, oy)
		end
	end
end

--every sprite updates its entry in the layer's sprites table
function SpriteSystem:process(e, dt)
	local sprite, x, y, ox, oy = e.sprite, e.position.x, e.position.y, e.offset.x, e.offset.y
	self.spriteLayer.sprites.thisguy = {
		sprite = sprite,
		x = x,
		y = y, 
		ox = ox,
		oy = oy
	}
	--spriteLayer:draw() gets called before this function, so this gates against a nil crash
	self.spriteLayer.drawReady = true
end

return SpriteSystem
But I guess my hesitance toward doing this could also be me getting used to systems in the ECS paradigm as being a collection of classes that represent relationships instead of objects. Weird, but interesting!
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 6 guests