Page 1 of 1

Problem with Space Invaders

Posted: Wed Jun 13, 2018 3:15 pm
by vSxo.lua
Hi. I'm not the best programmer and im not very good at LOVE 2D. So here is the problem. Im on the way of creating a clone of Space Invaders but i encountered a problem with asynchroonus enemies movement. When any enemy touches bounds of the screen function enemies_jump() is called, which calls Enemy:jump() for every enemy. This function is reversing speed of enemies and increasing thier Y position by 64. The middle row of enemies is moving in asynchronous way (it shouldn't). The X pos of most left enemy is weird compared to the rest of the aliens. I dont know why it is happening. They should move all together in the same pattern.

Can you help me? Im using classic.lua library.
Here is the code:

main.lua

Code: Select all

Object = require "classic"
require "player"
require "bullet"
require "enemy"

-- constants
WINDOW_WIDTH = 1024
WINDOW_HEIGHT = 768


function love.load()
	-- window
	love.window.setMode(WINDOW_WIDTH, WINDOW_HEIGHT, {resizable = false, vsync = true})
	love.graphics.setDefaultFilter('nearest', 'nearest')
	love.window.setTitle("Space Invaders")
	
	-- graphics
	background = love.graphics.newImage("background.png")
	ground = love.graphics.newImage("ground.png")
	spritesheet = love.graphics.newImage("sprites.png")
	
	rocket = love.graphics.newQuad(0,0,16,16,spritesheet:getDimensions())
	alien1 = love.graphics.newQuad(16,0,16,16,spritesheet:getDimensions())
	alien2 = love.graphics.newQuad(32,0,16,16,spritesheet:getDimensions())
	alien3 = love.graphics.newQuad(48,0,16,16,spritesheet:getDimensions())
	bullet = love.graphics.newQuad(64,0,16,16,spritesheet:getDimensions())
	charge = love.graphics.newQuad(80,0,16,16,spritesheet:getDimensions())
	heart = love.graphics.newQuad(96,0,16,16,spritesheet:getDimensions())
	
	-- sounds
	shoot = love.audio.newSource("shoot.wav", "static")
	hit = love.audio.newSource("hit.wav", "static")
	gameover = love.audio.newSource("gameover.wav", "static")
	
	-- varibles
	player = Player(512, 576, 3)
	bullets = {} -- table of bullets	
	enemies = {}
	
	spawn_enemies()
end

function spawn_enemies()
	enemy_speed = 150
	table.insert(enemies, Enemy(0, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(64, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(128, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(192, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(256, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(320, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(384, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(448, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(512, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(576, 0, "alien3", enemy_speed))
	table.insert(enemies, Enemy(0, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(64, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(128, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(192, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(256, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(320, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(384, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(448, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(512, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(576, 64, "alien2", enemy_speed))
	table.insert(enemies, Enemy(0, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(64, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(128, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(192, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(256, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(320, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(384, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(448, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(512, 128, "alien1", enemy_speed))
	table.insert(enemies, Enemy(576, 128, "alien1", enemy_speed))
end

function enemies_jump()
	for i, enemy in pairs (enemies) do
		enemy:jump()
		print("Enemy"..i.. " x:" .. enemy.x .. " y: " .. enemy.y .. " speed: " .. enemy.x_speed)
	end
end

function love.update(dt)
	player:update(dt)
	for index, bullet in pairs (bullets) do 
		bullet:update(dt)
		if bullet.y + bullet.height < 0 then
			table.remove(bullets, index)
		end
	end
	for i, enemy in pairs (enemies) do
		enemy:update(dt)
	end
end

function love.draw()
	love.graphics.draw(background, 0, 0)
	love.graphics.draw(ground, 0, 640, 0, 4, 4)
	player:draw()
	for index,bullet in pairs (bullets) do
		bullet:draw()
	end
	for i, enemy in pairs (enemies) do
		enemy:draw()
	end
end
player.lua

Code: Select all

Player = Object:extend()
require "bullet"

function Player:new(x, y, hearts)
	self.x = x
	self.y = y
	self.width = 64
	self.height = 64
	self.hearts = hearts
	self.speed = 300
	self.cooldown = 0
end

function Player:shot(self)
	table.insert(bullets, Bullet(self.x, self.y - 48))
	self.cooldown = 0.5
end

function Player:update(dt)
	self.cooldown = self.cooldown - dt
	if self.x + self.width > WINDOW_WIDTH then
		self.x = WINDOW_WIDTH - self.width
	end
	if self.x < 0 then
		self.x = 0
	end
	if love.keyboard.isDown("left") then
		self.x = self.x - self.speed * dt
	elseif love.keyboard.isDown("right") then
		self.x = self.x + self.speed * dt
	end
	if love.keyboard.isDown("space") and self.cooldown < 0 then
		Player:shot(self)
	end
end

function Player:draw()
	for i = 1, self.hearts do
		love.graphics.draw(spritesheet, heart, (i - 1) * 64, WINDOW_HEIGHT - 64, 0, 4, 4)
	end
	love.graphics.draw(spritesheet, rocket, self.x, self.y, 0, 4, 4)
end
bullet.lua

Code: Select all

 Bullet = Object:extend()
 
 function Bullet:new(x, y)
	self.x = x
	self.y = y 
	self.width = 20
	self.height = 40
	self.speed = 460
 end
 
 function Bullet:update(dt)
	self.y = self.y - (self.speed * dt)
 end
 
 function Bullet:draw()
	love.graphics.draw(spritesheet, bullet, self.x + 32, self.y + 32, 0, 4, 4, 8,8)
 end
enemy.lua

Code: Select all

 Enemy = Object:extend()

enemies_count = 0

function Enemy:new(x, y, typeof, x_speed)
	self.x = x
	self.y = y
	self.typeof = typeof
	self.x_speed = x_speed
	enemies_count = enemies_count + 1
end

function Enemy:jump()
	self.y = self.y + 64
	self.x_speed = - self.x_speed
end

function Enemy:update(dt)
	self.x = self.x + (self.x_speed * dt)
	if self.x <= 0 then
		self.x = 0
		enemies_jump()
	end
	if self.x + 64 >= WINDOW_WIDTH then
		self.x = WINDOW_WIDTH - 64
		enemies_jump()
	end
end

function Enemy:draw()
	if self.typeof == "alien1" then
	  love.graphics.draw(spritesheet, alien1, self.x, self.y, 0, 4, 4)
	  elseif self.typeof == "alien2" then
	 	love.graphics.draw(spritesheet, alien2, self.x, self.y, 0, 4, 4)
	  else
		love.graphics.draw(spritesheet, alien3, self.x, self.y, 0, 4, 4)
	end
end
Whole project is attached in zip file ;)
Thank you in advance.

Re: Problem with Space Invaders

Posted: Wed Jun 13, 2018 8:45 pm
by pgimeno
Hi, welcome to the forums!

I believe the cause of the desync is that, by the time you detect that one alien is out of the screen, you have already moved several others. This is made worse by the fact that you're using pairs() instead of ipairs(), and pairs() visits the elements in a random-ish order. But it still happened when I switched to ipairs() so that's not the only cause.

One of several possible solutions is to have a single source of positions. I mean, imagine the aliens are in a box. You move this box, not the aliens. You just place the aliens in a position that is always the same relative to the box. When an alien is killed, you recalculate the box dimensions, just in case it was the last one in the border.

Another possible, perhaps simpler solution is to set a flag when one alien goes out of the screen, instead of correcting it immediately, and when finishing updating, if the flag is set, correct all of them by the same amount.

Re: Problem with Space Invaders

Posted: Thu Jun 14, 2018 10:55 am
by vSxo.lua
Thank you pgimeno! I did it with flag setting and now it's working. Now I know why it was not working. I realized this when i changed pairs() to ipairs(). Basically when enemies were hitting right side for the first time only 10 of 30 were updated. When they hit the left side 11 of 30 were updated because loop was broken. :3