Code improvement (First time Lua-enet)

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
User avatar
ThatBonk
Prole
Posts: 11
Joined: Thu May 23, 2019 6:11 pm

Code improvement (First time Lua-enet)

Post by ThatBonk »

Hey guys!

I've been into Love2D for quiete a while now and decided to come in the forums to ask for some suggestions and improvements, especially on this program I just made. It's a simple 2D pong which has the focus on just gameplay and playing with other people.

Please give me feedback on the structure and possible better coding solutions.. I think I took way too long to write the code.

Server conf.lua:

Code: Select all

function love.conf(tbl)
    tbl.window.width = 1200
    tbl.window.height = 700
    tbl.window.x = 100
    tbl.window.y = 100
    tbl.window.title = "Server Pong"
    tbl.console = false
end
Server main.lua:

Code: Select all

-- Pong program server --


enet = require "enet"
ipAndPort = "localhost:11000"
connectionStatus = "Offline"
inputText = ""
hostExist = false

connected = false
pointsP1 = 0
pointsP2 = 0

player1X, player1Y, player1W, player1H, player1S = '40', '290', '20', '80', '200'
player2X, player2Y, player2W, player2H, player2S = '1140', '290', '20', '80', '200'


function love.load()
  
  -- Relevant game information --
  ballX, ballY, ballW, ballH, ballS = '570', '290', '20', '20','500'
  ballMovingLeft = false
  ballMovingUp = false
  gameStarted = false
  ready = false
  otherReady = false
end

function love.update(dt)


  if hostExist == true then
    local event = host:service(0)
    while event do
    
    dataPacket = split(event.data, '-')
    
    -- Unpack the received data --
    if event.type == "receive" then
      if dataPacket[1] == '1' then
        player2X, player2Y, player2W, player2H = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
      end
      
      if dataPacket[1] == '2' then
        player2X, player2Y, player2W, player2H = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
      end
      
      if dataPacket[1] == '3' then
      end
      
      if dataPacket[1] == '4' then
        otherReady = true
      end
      
      if dataPacket[1] == '5' then
        otherReady = false
      end
    end
    
      if event.type == "connect" then
        print(event.peer, "connected.")
      connected = true
      connectionStatus = "Online"
      client = event.peer
    end
    
      if event.type == "disconnect" then
        print(event.peer, "disconnected.")
      connected = false
      connectionStatus = "Offline"
      end	
      event = host:service()
    end
  end
  
  
  -- Player inputs --

    if love.keyboard.isDown("down") then 
      player1Y = player1Y + player1S * dt
      if 620 <=  tonumber(player1Y) + tonumber(player1H) then
        player1Y = 620 - tonumber(player1H)
      end
    end
  
    if love.keyboard.isDown("up") then
      player1Y = player1Y - player1S * dt
      if 20 >= tonumber(player1Y) then
        player1Y = '20'
      end
    end
    
    if love.keyboard.isDown("left") then
      player1W = player1W + 1
    end
    if love.keyboard.isDown("right") then
      player1H = player1H + 1
    end
  
  -- When connection establishes --
  
	if connected == true then
    
    if gameStarted == true then
      -- Ball Logic --
    
      if ballMovingLeft == true then
        ballX = ballX - ballS * dt
      else
        ballX = ballX + ballS * dt
      end
      
      if ballMovingUp == true then
        ballY = ballY - ballS * dt
      else
        ballY = ballY + ballS * dt
      end
      
      -- Ball collides with upper and lower borders --
      if ballY <= 20 then
        ballMovingUp = false
      end
      
      if ballY >= 600 then
        ballMovingUp = true
      end
      
      -- Ball collides with right or left border --
      if tonumber(ballX) <= 20 then
        pointsP2 = pointsP2 + 1 
        client:send('6-' .. pointsP1 .. '-' .. pointsP2)
        love.load()
      end
      
      if tonumber(ballX) >= 1160 then
        pointsP1 = pointsP1 + 1 
        client:send('6-' .. pointsP1 .. '-' .. pointsP2 )
        love.load()
      end
      
      -- Ball collides with player 1 and 2 --
      if tonumber(ballX) <= tonumber(player1X) + tonumber(player1W) and tonumber(ballY) <= tonumber(player1Y) + tonumber(player1H) and tonumber(ballY) + tonumber(ballH) >= tonumber(player1Y) then
        ballMovingLeft = false
      end
      
      if ballX + ballW >= tonumber(player2X) and ballY <= tonumber(player2Y) + tonumber(player2H) and ballY + ballH >= tonumber(player2Y) then
        ballMovingLeft = true
      end
      
    end
    
    -- End of ball logic --
    
    if ready == true and otherReady == true then
      gameStarted = true
      client:send('3')
    end
    
    -- Send positions to client --
  	player1Positions = '1' .. '-'	.. player1X .. '-' .. player1Y .. '-' .. player1W .. '-' .. player1H
    ballPositions = '2' .. '-'	.. ballX .. '-' .. ballY .. '-' .. ballW .. '-' .. ballH
    client:send(player1Positions)
    client:send(ballPositions)
	end
  
end

function love.keypressed(key, scancode, isrepeat)
    if key == "escape" then
      if connected == true then
        client:disconnect()
        connected = false
        hostExist = false
        connectionStatus = "Offline"
        love.load()
      end
    end
    
    if connected == true then
      if gameStarted == false then
        if key == "r" then
          if ready == false then
            ready = true
            client:send('4')
          else
            ready = true
            client:send('5')
          end
        end
      end
    else
      if key == "return" then
        ipAndPort = inputText
        host = enet.host_create(ipAndPort)
        inputText = ""
        hostExist = true
      end
    end
end


function love.textinput(input)
  if connected == false then
    inputText = inputText .. input
  end
end

function love.draw()
  
  -- Drawing players --
	love.graphics.setColor(0, 0, 255)
	love.graphics.rectangle("fill", player1X, player1Y, player1W, player1H)
	love.graphics.setColor(255, 0, 0)
	love.graphics.rectangle("fill", player2X, player2Y, player2W, player2H)
  
  -- Drawing borders --
	love.graphics.setColor(255, 255, 255)
  love.graphics.rectangle("line", 20, 20, 1160, 600) 
  
  -- Drawing ball --
  love.graphics.rectangle("fill", ballX, ballY, ballW, ballH)
  
  -- Drawing prints --
	if connected == false then
		love.graphics.setColor(255, 0, 0)
	else
		love.graphics.setColor(0, 255, 0)
	end
	love.graphics.print(connectionStatus, 20, 660, 0, 2)
  
  if gameStarted == false then
    if ready == false then
      love.graphics.setColor(255, 0, 0)
    else
      love.graphics.setColor(0, 255, 0)
    end
    love.graphics.print("Ready", 20, 630, 0, 2)
    
    if otherReady == false then
      love.graphics.setColor(255, 0, 0)
    else
      love.graphics.setColor(0, 255, 0)
    end
    love.graphics.print("Ready", 1105, 630, 0, 2)
  end
  
  -- Printing score --
	love.graphics.setColor(0, 0, 255)
  love.graphics.print(pointsP1, 200, 630, 0, 4)
	love.graphics.setColor(255, 0, 0)
  love.graphics.print(pointsP2, 900, 630, 0, 4)
  
	love.graphics.setColor(255, 255, 255)
  love.graphics.print(inputText)
  love.graphics.print(ipAndPort, 300, 0)
  
end

function split(s, delimiter)
	result = {}
	for match in (s..delimiter):gmatch("(.-)"..delimiter) do
		table.insert(result, match)
	end
	return result
end
Client conf.lua:

Code: Select all

function love.conf(tbl)
    tbl.window.width = 1200
    tbl.window.height = 700
    tbl.window.x = 800
    tbl.window.y = 100
    tbl.window.title = "Client Pong"
    tbl.console = false
end
Client main.lua:

Code: Select all

-- Pong program client --

	enet = require "enet"
  ipAndPort = "localhost:11000"
  host = enet.host_create()
  server = host:connect(ipAndPort)
	connectionStatus = "Offline"
  inputText = ""
	
  hostExists = false
	connected = false
  pointsP1 = 0
  pointsP2 = 0
  
  player1X, player1Y, player1W, player1H, player1S = '40', '290', '20', '80', '200'
	player2X, player2Y, player2W, player2H, player2S = '1140', '290', '20', '80', '200'
  
	dataPacket = {}
	dataPacket[1], dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5] = '0', '0', '0', '0' , '0'

function love.load()	
  ballX, ballY, ballW, ballH = '570', '290', '20', '20'
  gameStarted = false
  ready = false
  otherReady = false
end

function love.update(dt)

  if hostExists == true then
    event = host:service(0)
  end
  
  while event do
  
	dataPacket = split(event.data, '-')
  
    if event.type == "receive" then
      
		if dataPacket[1] == '1' then
			player1X, player1Y, player1W, player1H = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
		end
    
    if dataPacket[1] == '2' then
			ballX, ballY, ballW, ballH = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
		end
    
    if dataPacket[1] == '3' then
      gameStarted = true
		end
    
    if dataPacket[1] == '4' then
			otherReady = true
		end
    
    if dataPacket[1] == '5' then
			otherReady = false
		end
    
    if dataPacket[1] == '6' then
      pointsP1, pointsP2 = dataPacket[2], dataPacket[3]
			love.load()
		end
    
	end
	
    if event.type == "connect" then
      print(event.peer, "connected.")
      event.peer:send( "ping" )
	  data = event.data
	  connected = true
	  connectionStatus = "Online"
    end 
	
	if event.type == "disconnect" then
      print(event.peer, "disconnected.")
	  connected = false
	  connectionStatus = "Offline"
    end
    event = host:service()
  end
  
    if love.keyboard.isDown("down") then 
      player2Y = player2Y + player2S * dt
      if 620 <=  tonumber(player2Y) + tonumber(player2H) then
        player2Y = 620 - tonumber(player2H)
      end
    end
  
    if love.keyboard.isDown("up") then
      player2Y = player2Y - player2S * dt
      if 20 >= tonumber(player2Y) then
        player2Y = '20'
      end
    end
  
  if connected == true then
    
		player2Positions = '1' .. '-'	.. player2X .. '-' .. player2Y .. '-' .. player2W .. '-' .. player2H
		server:send(player2Positions)
	end
  
end

function love.keypressed(key, scancode, isrepeat)
    if key == "escape" then
      server:disconnect()
      connected = false
      hostExist = false
      connectionStatus = "Offline"
      love.load()
    end
    
    if connected == true then
      if gameStarted == false then
        if key == "r" then
          if ready == false then
            ready = true
            server:send('4')
          else
            ready = false
            server:send('5')
          end
        end
      end
    else
      if key == "return" then
        ipAndPort = inputText
        server = host:connect(ipAndPort)
        inputText = ""
        hostExists = true
      end
    end
end

function love.textinput(input)
  if connected == false then
    inputText = inputText .. input
  end
end


function love.draw()

	love.graphics.setColor(0, 0, 255)
	love.graphics.rectangle("fill", player1X, player1Y, player1W, player1H)
	love.graphics.setColor(255, 0, 0)
	love.graphics.rectangle("fill", player2X, player2Y, player2W, player2H)
	love.graphics.setColor(255, 255, 255)
  love.graphics.rectangle("line", 20, 20, 1160, 600) 
  
  love.graphics.rectangle("fill", ballX, ballY, ballW, ballH)
	
	if connected == false then
		love.graphics.setColor(255, 0, 0)
	else
		love.graphics.setColor(0, 255, 0)
	end
	love.graphics.print(connectionStatus, 20, 660, 0, 2)
  
  if gameStarted == false then
    if ready == false then
      love.graphics.setColor(255, 0, 0)
    else
      love.graphics.setColor(0, 255, 0)
    end
    love.graphics.print("Ready", 1105, 630, 0, 2)
  
    if otherReady == false then
      love.graphics.setColor(255, 0, 0)
    else
      love.graphics.setColor(0, 255, 0)
    end
    love.graphics.print("Ready", 20, 630, 0, 2)
  end
  
	love.graphics.setColor(0, 0, 255)
  love.graphics.print(pointsP1, 200, 630, 0, 4)
	love.graphics.setColor(255, 0, 0)
  love.graphics.print(pointsP2, 900, 630, 0, 4)
  
  love.graphics.setColor(255, 255, 255)
  love.graphics.print(inputText)
  love.graphics.print(ipAndPort, 300, 0)
	
end

function split(s, delimiter)
	result = {}
	for match in (s..delimiter):gmatch("(.-)"..delimiter) do
		table.insert(result, match)
	end
	return result
end
User avatar
yintercept
Citizen
Posts: 64
Joined: Mon Apr 02, 2018 3:31 pm

Re: Code improvement (First time Lua-enet)

Post by yintercept »

Other than the obvious "nothing matters and you're not out of preproduction until it's fun", I'd say just disable the print statements in the draw loop (thats obvious too).

I'm interested to know what your experience with enet was. If you encountered problems, what they were, what the bugs were, and so on.

Also welcome to the forum.
Back in the saddle again.
User avatar
ThatBonk
Prole
Posts: 11
Joined: Thu May 23, 2019 6:11 pm

Re: Code improvement (First time Lua-enet)

Post by ThatBonk »

So far, it was a fine experience, especially since the documentary is in my opinion described better than in luasocket. The first real problem I encountered is the following:

When testing it out and letting someone else connect through my external IP to host the game, at some point sooner or later, the client just gets disconnected out of nowhere.

And thanks for the greetings. I am glad I can join a community with people who like to create 2D games.
User avatar
yintercept
Citizen
Posts: 64
Joined: Mon Apr 02, 2018 3:31 pm

Re: Code improvement (First time Lua-enet)

Post by yintercept »

*the client just gets disconnected out of nowhere.*

Sounds like a keep alive problem.

Enet is udp right? If so, try periodically sending a keep alive packet and see what happens then.
Back in the saddle again.
TheHUG
Citizen
Posts: 61
Joined: Sun Apr 01, 2018 4:21 pm

Re: Code improvement (First time Lua-enet)

Post by TheHUG »

Hey ThatBonk,
Good on you for asking for coding feedback :)! Getting feedback is really important in my experience.
I'll get the readability nitpicks out of the way first:
make sure your indentation is consistent, it's important for readability. When someone (including you) scans your code it's easier to infer the start and end of code blocks (function, for, if, etc.) from indentation than from counting 'end' statements.
for example, I read

Code: Select all

  
    if event.type == "connect" then
      print(event.peer, "connected.")
      event.peer:send( "ping" )
	  data = event.data
	  connected = true
	  connectionStatus = "Online"
    end 
 
as

Code: Select all

    if event.type == "connect" then
      print(event.peer, "connected.")
      event.peer:send( "ping" ,
	  data = event.data,
	  connected = true,
	  connectionStatus = "Online")
    end 
 
Which doesn't even make sense in lua, so I had to do a double take make sure I knew what I was reading.

Code: Select all

    if event.type == "connect" then
        print(event.peer, "connected.")
        event.peer:send( "ping" )
        data = event.data
        connected = true
	connectionStatus = "Online"
    end 
 
Second, it's a little controversial (so keep your grain of salt ready) but I'm a major proponent of the 80 column rule (https://www.emacswiki.org/emacs/EightyColumnRule, https://nickjanetakis.com/blog/80-chara ... even-today, https://stackoverflow.com/questions/110 ... -in-a-code). As are most coding standards.
TL;DR: 70-80 characters per line maximizes readability (which is why books are generally printed at ~70 characters per line, and there is a similar principle in web design) and allows you to easily have multiple files open side-by-side.
I usually have three things open side by side (e.g. terminal, tests, source code), or when I'm chillin' I just have one thing in a small window so I can watch youtube with the rest of my screen.

What text editor are you using? Many text editors will help you with these to so that you barely have to think about them.

For code organization:
I notice you have all of your game loop explicitly stated in love.update. It's best to break it down into parts, and run each of those parts in love.update. Programming is a process of breaking complex problems down into simple ones. The simplest thing is just to take parts of that code and put each part in a function. For larger projects this generally doesn't cut it, which is why Object Oriented Programming(OOP) and Entity-Component-System(ECS) architectures are a thing. If you're new to programming in general, don't worry too much about these yet. I'll give a brief explanation though in case you're curious.

In OOP, you might (for example) create a table for ball, client-controller, server, and player, each with their own `:update(dt)` and `:update(dt)` methods, which you'd call in the love.update loop. There's a lot more to OOP than that, but I don't want to go on too long in case you're already familiar.

I'm not as familiar with ECS, but I'm trying to get into it as it seems better for games than conventional OOP. The general principle is that you break down all the processes of your game code into 'systems' (e.g. a movementSystem, a collisionSystem, controllerSystem, communicationSystem, renderingSystem) and the data pertaining to each of these is contained in components (e.g. position, boundingBox, etc.). Instances of these components are attached to the actual in-game entities (e.g. ball, player) to enable the systems to act on them.

Small note about these ifs:

Code: Select all

dataPacket = split(event.data, '-')
  
    if event.type == "receive" then
      
		if dataPacket[1] == '1' then
			player1X, player1Y, player1W, player1H = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
		end
    
    if dataPacket[1] == '2' then
			ballX, ballY, ballW, ballH = dataPacket[2], dataPacket[3], dataPacket[4], dataPacket[5]
		end
    
    if dataPacket[1] == '3' then
      gameStarted = true
		end
    
    if dataPacket[1] == '4' then
			otherReady = true
		end
    
    if dataPacket[1] == '5' then
			otherReady = false
		end
    
    if dataPacket[1] == '6' then
      pointsP1, pointsP2 = dataPacket[2], dataPacket[3]
			love.load()
		end
    
	end
(indentation again, but also:) this could be more concise with elseif

Code: Select all

if event.type == 'recieve' then
    if dataPacket[1] == '1' then
        -- do stuff
     elseif datapacket[1] == '1' then
        --do other stuff
     elseif datapacket[1] == '3' then
         ..... etc.
     ......
     end
 end
 
You could also consider using more descriptive strings than '1', '2'... if that's possible with enet.

Finally, I see some small bits of duplicated code, sometimes within files sometimes between client and server main.lua. For the former, thinking more about how you group your data should allow you to, for example do :

Code: Select all

        for n, player in ipairs(players) do
             love.graphics.setColor(player.color)
             love.graphics.rectangle('fill', player.x, player.y, player.w, player.h)
             end
Which means that if you want to change the way a player is drawn, you just have to change it once, rather than once for each player.
(obviously just one way to do it, and assumes you haven't decided to just write a player:draw() method)

For the latter, you could consider putting the duplicate code in different file and `require`-ing it from both main.lua.

Don't worry about taking long to code, these things always take longer than it feels like they should :). Even this post took me much longer than expected. It's better to take your time to think things through, learn, and reflect than to rush just to have a working project, especially when the goal is to learn.

Enjoy finalizing your Pong!
Last edited by TheHUG on Wed May 29, 2019 8:30 am, edited 1 time in total.
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Code improvement (First time Lua-enet)

Post by bobbyjones »

Do not know if you know this but you can run love in such a way that does not initialize the window for the server.
User avatar
ThatBonk
Prole
Posts: 11
Joined: Thu May 23, 2019 6:11 pm

Re: Code improvement (First time Lua-enet)

Post by ThatBonk »

A huge thanks for the feedback, TheHUG!

-> I actually have never heard in my entire hobby coding life, that the 80 character per line "rule" exists. I will definitly take this into condidiration for my future projects I want to tackle on.

-> I use Zerobrane Studios as my current editor.. but I am free to suggestions, if there are easier and more readable ones.

-> I have tried to follow a function structure once, like I would even call somehow close to object orientation? But that was also not a perfect solution. I will include how I tried to break it down into functions.

Space danger -> main.lua

Code: Select all

-- Space Danger--

  -- Required files are embedded here. They help to shorten the main --
  -- script significally and make the code more understandable.      --
  require "classes"

-- love.load loads all game data Variables and images. --
function love.load() 
  
  -- Pre-Game parameters --
  
  mainmenu = true
  gameOver = false
  levelTimer = 0
  
    -- Tables for all enemies that can be removed. --
    enemies = {}
    -- .. for bullets --
    bullets = {}
  
  -- Load game data --
  enemies_load()
  items_load()
  players_load()
  
  -- Load visuals --
  background_load()
  mainmenu_load()
  prints_load()
  
  -- Timer data --
  
  oneSecondTimerPulse = 0
  enemyTimerPulse = 0
  spawnController = 2
  
end

-- love.update updates variables and calculations. --
function love.update(dt)
  
  if mainmenu == false then
  
  -- Update class-specific data --
  players_update(dt)
  enemies_update(dt)
  items_update(dt)
  
  -- Update keypresses --
  
  
  -- Update visuals --
  background_update(dt)
  mainmenu_update(dt)
  prints_update(dt)
  
  -- Enemy spawn timer --
    enemyTimerPulse = enemyTimerPulse + spawnController * dt
      if enemyTimerPulse > 3 then
        
        if levelTimer <= 15 then
          enemyType = 1
          enemySpawnIntervall = 1
        end
        
        if levelTimer <= 30 and levelTimer >= 15 then
          spawnController = 10
          enemyType = 2
          enemySpawnIntervall = 1
        end
        
        if levelTimer <= 45 and levelTimer >= 30 then
          spawnController = 6
          enemyType = 3
          enemySpawnIntervall = 1
        end
        
        if levelTimer <= 60 and levelTimer >= 45 then
          spawnController = 12
          enemyType = 4
          enemySpawnIntervall = 1
        end
        
        if levelTimer > 60 then
          spawnController = 8
          enemyType = math.random(1,4)
          enemySpawnIntervall = 1
        end
        
        if levelTimer > 90 then
          spawnController = 10
          enemyType = math.random(1,4)
          enemySpawnIntervall = 2
        end
        
        for i = 1, enemySpawnIntervall do
          enemy_spawn()
        end
        
        enemyTimerPulse = 0
        
      end
  
  end -- End of main menu freeze

  -- Timer --
  
    oneSecondTimerPulse = oneSecondTimerPulse + 2 * dt
      if oneSecondTimerPulse > 2 then -- Seperate second 1 and 2
        oneSecondTimerPulse = 0
        if mainmenu == false then
            if p1Dead == false then
              levelTimer = levelTimer + 1
            end
        end
      end
      
    levelTimer = math.floor(levelTimer)
    fps = love.timer:getFPS()
  
end

-- love.mousepressed checks mouseinput. --
function love.mousepressed(x, y, button, istouch)

end

-- love.keypressed checks if a key had been pressed (and only acts on a single input) --
function love.keypressed(key, scancode, isrepeat)
  if mainmenu == true then
    if key == "space" then
      mainmenu = false
      levelTimer = 0
    end
  else
    if key == "escape" then
      love.load()
    end
  end
end

-- love.draw draws every visualization on the screen. --
function love.draw()
  
  -- Scrolling background --
  background_draw()
  
  -- Draw object-specific visuals --
  players_draw()
  enemies_draw()
  items_draw()
  
  -- Print-texts --
  prints_draw()
  
  -- Drawing the main Menu of the pre-game --
  mainmenu_draw()
    
end
Space Danger -> classes.lua

Code: Select all

-- Players --
  
function players_load()
  p1PositionX = 384
  p1PositionY = 584
  p1HitboxWidth = 32
  p1HitboxHeight = 32
  p1Speed = 200
  p1Dead = false
  p1Spawning = true
  p1Idle = true
  
  
  local frameWidth = 32
  local frameHeight = 32
  
  p1Sprite = love.graphics.newImage("shipP1.png")
  
  p1Frames = {}
  
    for i=0,9 do
        for j=0,9 do
            table.insert(p1Frames, love.graphics.newQuad(j * frameWidth, i * frameHeight, 32, 32, p1Sprite:getWidth(), p1Sprite:getHeight()))
        end
    end

    currentP1Frame = 1
  
end

function players_update(dt)
  
  -- Logic --
  
  if p1Spawning == false and gameOver == false then
  
    if love.keyboard.isDown("left") and p1PositionX > 0 then
      p1PositionX = p1PositionX - p1Speed * dt
      p1Idle = false
        if oneSecondTimerPulse > 1  and p1Spawning == false then -- Seperate second 1 and 2
          currentP1Frame = 16
        else
          currentP1Frame = 15
        end
    end
    
    if love.keyboard.isDown("right") and p1PositionX < 767 then
      p1PositionX = p1PositionX + p1Speed * dt
      p1Idle = false
        if oneSecondTimerPulse > 1 and p1Spawning == false then -- Seperate second 1 and 2
          currentP1Frame = 14
        else
          currentP1Frame = 13
        end
    end
    
    if love.keyboard.isDown("down") and p1PositionY < 667 then
      p1PositionY = p1PositionY + p1Speed * dt
    end
    
    if love.keyboard.isDown("up") and p1PositionY > 0 then
      p1PositionY = p1PositionY - p1Speed * dt
    end
    
    for i, parent in ipairs(enemies) do
      if p1PositionX < parent.positionX + parent.width and p1PositionX + p1HitboxWidth > parent.positionX and p1PositionY < parent.positionY + parent.height 
      and p1PositionY + p1HitboxHeight > parent.positionY then
        p1Dead = true
      end
    end
  
  end
  
  -- Animations --
  
  if mainmenu == false and p1Spawning == true then
    currentP1Frame = currentP1Frame + 10 * dt
      if currentP1Frame > 11 then
        p1Spawning = false
      end
  end
  
  if p1Spawning == false and p1Idle == true and p1Dead == false then
      if oneSecondTimerPulse > 1 then -- Seperate second 1 and 2
        currentP1Frame = 12
      else
        currentP1Frame = 11
      end
  end
  
  if p1Dead == true then
    gameOver = true
    currentP1Frame = currentP1Frame - 10 * dt
      if currentP1Frame < 1 then
        currentP1Frame = 1
      end
  end
  
end

function love.keyreleased(key)
  p1Idle = true
end

function players_draw()
  if p1Dead == false then
    love.graphics.rectangle("line", p1PositionX, p1PositionY, p1HitboxWidth, p1HitboxHeight)
  end
  
  if mainmenu == false then
    love.graphics.draw(p1Sprite, p1Frames[math.floor(currentP1Frame)], p1PositionX, p1PositionY)
  end
  
end

----------------------------------------------------------------------
-- Enemies --

function enemies_load()
  enemyType = 1
  enemySpawnIntervall = 1
end

function enemy_spawn()
  
  normalEnemies = {}
  
  -- This property will be counted for every enemy-type --
  normalEnemies.positionY = -32
  normalEnemies.positionX = math.random(0, 767)
  normalEnemies.width = 32
  normalEnemies.height = 32
  normalEnemies.left = false
  
  normalEnemies.startPath = normalEnemies.positionX
  
  -- Here are properties for specific arguements for the enemies to their type --
  
    -- Balanced blaster --
    if enemyType == 1 then
      normalEnemies.enemyType = 1
      normalEnemies.speed = 100
      normalEnemies.health = 3
    end
    
    -- Strong blaster --
    if enemyType == 2 then
      normalEnemies.enemyType = 2
      normalEnemies.speed = 70
      normalEnemies.health = 2
    end
    
    -- Speedy blaster --
    if enemyType == 3 then
      normalEnemies.enemyType = 3
      normalEnemies.speed = 200
      normalEnemies.health = 1
    end
    
    -- Defensive blaster --
    if enemyType == 4 then
     
      if normalEnemies.startPath < 400 then
        normalEnemies.positionX = -32
        normalEnemies.left = false
      else
        normalEnemies.positionX = 832
        normalEnemies.left = true
      end
  
      normalEnemies.enemyType = 4
      normalEnemies.speed = 100
      normalEnemies.health = 5
      
      normalEnemies.positionY = math.random(0, 667)
    end
  
  table.insert(enemies, normalEnemies)
  end

function enemies_update(dt)
  
  for i, parent in ipairs(enemies) do
    
    if parent.enemyType == 1 then
        parent.positionY = parent.positionY + parent.speed * dt
        if parent.startPath < 400 then
          if parent.positionX < parent.startPath + 100 then
            parent.positionX = parent.positionX + parent.speed * dt
          end
        else
          if parent.positionX > parent.startPath - 100 then
            parent.positionX = parent.positionX - parent.speed * dt
          end
        end
    end
    
    if parent.enemyType == 2 then
      parent.positionY = parent.positionY + parent.speed * dt
    end    
    
    if parent.enemyType == 3 then
      parent.positionY = parent.positionY + parent.speed * dt
    end   
    
    if parent.enemyType == 4 then
      if parent.left == false then
        parent.positionX = parent.positionX + parent.speed * dt
      else
        parent.positionX = parent.positionX - parent.speed * dt
      end
    end
    
    
    -- Remove the enemies that go out of border. --
    for j = #enemies, 1, -1 do
      if enemies[j].positionX < -40 or  enemies[j].positionX > 840 or  enemies[j].positionY < -40 or  enemies[j].positionY > 740 then
        table.remove(enemies, j)
      end
    end
  end
  
end

function enemies_draw()
  
  for i,parent in ipairs(enemies) do
    
    if parent.enemyType == 1 then
      love.graphics.setColor(255, 255, 255)
      love.graphics.rectangle("fill", parent.positionX, parent.positionY, parent.width, parent.height)
    end
    
    if parent.enemyType == 2 then
      love.graphics.setColor(255, 0, 0)
      love.graphics.rectangle("fill", parent.positionX, parent.positionY, parent.width, parent.height)
    end
    
    if parent.enemyType == 3 then
      love.graphics.setColor(0, 255, 0)
      love.graphics.rectangle("fill", parent.positionX, parent.positionY, parent.width, parent.height)
    end
    
    if parent.enemyType == 4 then
      love.graphics.setColor(0, 0, 255)
      love.graphics.rectangle("fill", parent.positionX, parent.positionY, parent.width, parent.height)
    end
    
  end
  
end

----------------------------------------------------------------------
-- Items --

function items_load()
  itemSprite = love.graphics.newImage("item.png")
  
  items = {}
  
  for i = 0, 3, 1 do
      table.insert(items, love.graphics.newQuad(64 * i, 0, 64, 64, itemSprite:getWidth(), itemSprite:getHeight()))
  end
  
  currentItemFrame = 1
  
end

function items_update(dt)

end

function items_draw()
  
  -- Black box for GUI elements --
    -- Border --
    love.graphics.setColor(105, 105, 105)
    love.graphics.rectangle("fill", 0, 700, 800, 5)
    -- Black background --
    love.graphics.setColor(0, 0, 0)
    love.graphics.rectangle("fill", 0, 705, 800, 95)
    
  -- Item box --
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(itemSprite, items[currentItemFrame], 720, 720)
end

----------------------------------------------------------------------
-- Background --

function background_load()
  background = love.graphics.newImage("space.png")
  scrollingBackground1y = 0
  scrollingBackground2y = -800
  scrollingBackgroundSpeed = 100
end

function background_update(dt)
  scrollingBackground1y = scrollingBackground1y + scrollingBackgroundSpeed * dt
  scrollingBackground2y = scrollingBackground2y + scrollingBackgroundSpeed * dt
  
  if scrollingBackground1y > 800 then
    scrollingBackground1y = -800
  end
  
  if scrollingBackground2y > 800 then
    scrollingBackground2y = -800
  end
end

function background_draw()
  love.graphics.draw(background, 0, scrollingBackground1y)
  love.graphics.draw(background, 0, scrollingBackground2y)
end

----------------------------------------------------------------------
-- Main menu --

function mainmenu_load()
  
end

function mainmenu_update(dt)

end

function mainmenu_draw()
  if mainmenu == true then
    
    love.graphics.setColor(0,0,0)
    love.graphics.rectangle("fill", 0, 0, 800, 800)
    
    love.graphics.setColor(255,0,0)
    love.graphics.print("Space Danger", 270, 100, 0, 3)
    
      if oneSecondTimerPulse > 1 then
        love.graphics.setColor(255,255,255)
        love.graphics.print("Press 'space' to start the game.", 215, 350, 0, 2)
      end
      
  end
end

----------------------------------------------------------------------
-- Prints --

function prints_load()
  
end

function prints_update(dt)

end

function prints_draw()
  if mainmenu == false then
    love.graphics.print("Time passed: " .. levelTimer, 10, 720)
    
    
    if p1Dead == false then
      if levelTimer > 0 and levelTimer < 15 then
        love.graphics.print("Getting started.", 10, 740)
      end
      if levelTimer > 15 and levelTimer < 30 then
        love.graphics.print("New enemies.", 10, 740)
      end
      if levelTimer > 30 and levelTimer < 45 then
        love.graphics.print("Getting complicated..", 10, 740)
      end
      if levelTimer > 45 and levelTimer < 60 then
        love.graphics.print("Different strategies.", 10, 740)
      end
      if levelTimer > 60 and levelTimer < 90 then
        love.graphics.print("Mixing things up.", 10, 740)
      end
      if levelTimer > 90 then
        love.graphics.print("Can you survive?", 10, 740)
      end
    else
      love.graphics.print("Game over.", 10, 740)
    end
    
    love.graphics.print("FPS: " .. fps, 10, 780)
  end
end
And for bobbyjones, yeah, I know about that. The pong game though was played with the server and the client together and only meant for one client to play with the server.
TheHUG
Citizen
Posts: 61
Joined: Sun Apr 01, 2018 4:21 pm

Re: Code improvement (First time Lua-enet)

Post by TheHUG »

I've heard good things about ZeroBrane. I imagine I imagine it can automatically correct your indentation when you press tab. You can also configure it to tell you when you go over 80 characters: https://studio.zerobrane.com/doc-editor-preferences (editor.edge=true).

Figuring out how to break things down in your code is tricky, but one basic guiding principle is DRY: Don't Repeat Yourself. I haven't taken the time to fully understand Space Danger, but I do see that enemies and the players have similar code in how they move (but obviously different in how that movement is determined). It can work as an example. If both player and enemies move, you'd generally abstract movement to a function and run it on all 'moveable' objects. In OOP you'd attach this `moveable:move()` method to moveables one way (composition) or another (inheritance), and have `self:move` in their `update` method. In ECS you might have some components containing data important to moving (such as `positon` and `velocity`), on which the `movementSystem` acts. These components are bound to an entity representing the in-game er... thing? (enemy, player, whatever else moves). There are other paradigms, but i don't think they're common in game development, and I'm not that familiar with them.

OOP is the most common paradigm, but it seems to be on the way out for game development, with ECS picking up popularity (and outside of games, Data Driven Development and Functional (declarative) Programming). After doing one way for a while it can become tricky to reason about code in a different way (though imperative is always doable), so you may have an advantage if you go to ECS first.
Post Reply

Who is online

Users browsing this forum: No registered users and 20 guests