Advertisement
Xelostar

ThreeD_API_v0.5.1_Engine

Jul 22nd, 2018
653
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.56 KB | None | 0 0
  1.  
  2. -- Made by Xelostar: https://www.youtube.com/channel/UCDE2STpSWJrIUyKtiYGeWxw
  3.  
  4. local path = "/"..shell.dir() -- path to this program
  5.  
  6. os.loadAPI(path.."/ThreeD") -- loading the APIs
  7. os.loadAPI(path.."/bufferAPI")
  8. os.loadAPI(path.."/blittle")
  9.  
  10. local objects = { -- all objects in the game
  11.     {model = "box", x = 4, y = 0.5, z = 0, solid = true}, -- solid = true for all "wall" blocks for collision
  12.     {model = "box", x = 4, y = 1.5, z = 0, solid = true},
  13.     {model = "box", x = 4, y = 0.5, z = 1, solid = true},
  14.     {model = "box", x = 4, y = 0.5, z = 2, solid = true},
  15.     {model = "box", x = 4, y = 0.5, z = 3, solid = true},
  16.     {model = "box", x = 3, y = 0.5, z = 3, solid = true},
  17.     {model = "box", x = 2, y = 0.5, z = 3, solid = true},
  18.     {model = "box", x = 1, y = 0.5, z = 3, solid = true},
  19.     {model = "box", x = 1, y = 1.5, z = 3, solid = true},
  20.     {model = "pineapple", x = 2, y = 0, z = 1, rotationY = 0},
  21. }
  22.  
  23. local playerX = -3 -- player position
  24. local playerY = 0
  25. local playerZ = 1
  26.  
  27. local playerSpeed = 3 -- player movement speed
  28. local playerTurnSpeed = 180 -- player turn speed
  29.  
  30. local keysDown = {} -- keysDown[key] is true if the key is down (for smooth input)
  31. local blockThickness = 0.9 -- the size of the hitbox for the solid blocks from the middle of the object
  32.  
  33. local FoV = 90 -- field of view
  34.  
  35. local playerDirectionHor = 0 -- camera direction
  36. local playerDirectionVer = 0
  37.  
  38. local screenWidth, screenHeight = term.getSize() -- size of the screen
  39.  
  40. local backgroundColor1 = colors.lime -- ground color
  41. local backgroundColor2 = colors.lightBlue -- sky color
  42.  
  43. local ThreeDFrame = ThreeD.newFrame(1, 1, screenWidth, screenHeight, FoV, playerX, playerY + 0.5, playerZ, playerDirectionVer, playerDirectionHor, path.."/models") -- making a new frame where the camera is 0.5 blocks above the player
  44. local blittleOn = true -- blittle is turned on by default
  45. ThreeDFrame:useBLittle(blittleOn) -- set the frame to use blittle
  46.  
  47. local totalTime = 0 -- time in seconds elapsed since the start of the program
  48.  
  49. local function rendering()
  50.     while true do
  51.         ThreeDFrame:loadGround(backgroundColor1)
  52.         ThreeDFrame:loadSky(backgroundColor2)
  53.         ThreeDFrame:loadObjects(objects)
  54.         ThreeDFrame:drawBuffer()
  55.  
  56.         os.queueEvent("FakeEvent") -- to prevent "too long without yielding" errors without slowing down the program
  57.         os.pullEvent("FakeEvent")  
  58.     end
  59. end
  60.  
  61. local function free(x, y, z) -- for collision detection. Tests to see if there's a hitbox (x, y, z) is in
  62.     for _, object in pairs(objects) do
  63.         if (object.solid) then
  64.             if (x >= object.x - blockThickness and x <= object.x + blockThickness) then
  65.                 if (y >= object.y - 1 and y <= object.y + 0.5 + 0.2) then -- the height is done differently, since the  player has to have a certain height (different collision from feet and head)
  66.                     if (z >= object.z - blockThickness and z <= object.z + blockThickness) then
  67.                         return false -- collision detected
  68.                     end
  69.                 end
  70.             end
  71.         end
  72.     end
  73.  
  74.     return true -- no collision detected
  75. end
  76.  
  77. local function inputPlayer(time) -- time is the elapsed time in seconds since the last time this function was executed
  78.     local dx = 0
  79.     local dy = 0
  80.     local dz = 0
  81.  
  82.     if (keysDown[keys.left]) then
  83.         playerDirectionHor = playerDirectionHor - playerTurnSpeed * time
  84.         if (playerDirectionHor <= -180) then
  85.             playerDirectionHor = playerDirectionHor + 360
  86.         end
  87.     end
  88.     if (keysDown[keys.right]) then
  89.         playerDirectionHor = playerDirectionHor + playerTurnSpeed * time
  90.         if (playerDirectionHor >= 180) then
  91.             playerDirectionHor = playerDirectionHor - 360
  92.         end
  93.     end
  94.     if (keysDown[keys.down]) then
  95.         playerDirectionVer = playerDirectionVer - playerTurnSpeed * time
  96.         if (playerDirectionVer < -80) then
  97.             playerDirectionVer = -80
  98.         end
  99.     end
  100.     if (keysDown[keys.up]) then
  101.         playerDirectionVer = playerDirectionVer + playerTurnSpeed * time
  102.         if (playerDirectionVer > 80) then
  103.             playerDirectionVer = 80
  104.         end
  105.     end
  106.     if (keysDown[keys.w]) then
  107.         dx = playerSpeed * math.cos(math.rad(playerDirectionHor)) + dx
  108.         dz = playerSpeed * math.sin(math.rad(playerDirectionHor)) + dz
  109.     end
  110.     if (keysDown[keys.s]) then
  111.         dx = -playerSpeed * math.cos(math.rad(playerDirectionHor)) + dx
  112.         dz = -playerSpeed * math.sin(math.rad(playerDirectionHor)) + dz
  113.     end
  114.     if (keysDown[keys.a]) then
  115.         dx = playerSpeed * math.cos(math.rad(playerDirectionHor - 90)) + dx
  116.         dz = playerSpeed * math.sin(math.rad(playerDirectionHor - 90)) + dz
  117.     end
  118.     if (keysDown[keys.d]) then
  119.         dx = playerSpeed * math.cos(math.rad(playerDirectionHor + 90)) + dx
  120.         dz = playerSpeed * math.sin(math.rad(playerDirectionHor + 90)) + dz
  121.     end
  122.    
  123.     if (keysDown[keys.space]) then
  124.         dy = playerSpeed + dy
  125.     end
  126.     if (keysDown[keys.leftShift]) then
  127.         dy = -playerSpeed + dy
  128.     end
  129.    
  130.     -- if where the player will move to is free (no collisions) then move there
  131.     if (free(playerX + dx * time, playerY, playerZ)) then
  132.         playerX = playerX + dx * time -- multiply by time so that dx becomes blocks per second
  133.     end
  134.     if (playerY + dy * time >= 0) then
  135.         if (free(playerX, playerY + dy * time, playerZ)) then
  136.             playerY = playerY + dy * time
  137.         end
  138.     end
  139.     if (free(playerX, playerY, playerZ + dz * time)) then
  140.         playerZ = playerZ + dz * time
  141.     end
  142.  
  143.     ThreeDFrame:setCamera(playerX, playerY + 0.5, playerZ, playerDirectionHor, playerDirectionVer) -- set the new camera position according to the player (again the height is 0.5 blocks above the feet)
  144. end
  145.  
  146. local function keyInput()
  147.     while true do
  148.         local event, key, x, y = os.pullEventRaw()
  149.  
  150.         if (event == "key") then -- detect key presses
  151.             keysDown[key] = true
  152.             if (key == keys.g) then -- if the button is supposed to be pressed once, we'll deal with the processing of the input here
  153.                 if (not blittleOn) then
  154.                     blittleOn = true
  155.                     ThreeDFrame:useBLittle(true)
  156.                 else
  157.                     blittleOn = false
  158.                     ThreeDFrame:useBLittle(false)
  159.                 end
  160.             end
  161.         elseif (event == "key_up") then -- detect key releases
  162.             keysDown[key] = nil
  163.         elseif (event == "mouse_click") then
  164.             local objectIndex, polyIndex = ThreeDFrame:getObjectIndexTrace(objects, x, y) -- detect on what and object the player clicked
  165.             if (objectIndex ~= nil) then -- if the player clicked on an object (not void)
  166.                 table.remove(objects, objectIndex) -- remove the object the player clicked on
  167.             end
  168.         end
  169.     end
  170. end
  171.  
  172. local function updateGame(time) -- time is the elapsede time in seconds since the last time this function was executed
  173.     totalTime = totalTime + time -- increase the total time that has elapsed
  174.     for objectNr, object in pairs(objects) do
  175.         if (object.model == "pineapple") then -- for all pineapples:
  176.             object.y = math.sin(totalTime*2)/4 -- set their height according to totalTime to make them smoothly bob up and down
  177.             object.rotationY = object.rotationY + 50*time -- increase their rotation according to the time that has elapsed (50 degrees per second of rotation)
  178.         end
  179.     end
  180. end
  181.  
  182. local function gameUpdate() -- this function uses some trickery to estimate the time elapsed more precise than 1 Minecraft tick (0.05 seconds)
  183.     -- a lot of the time, withing each game tick about three frames are rendered which means that the accuracy is more like 0.02 seconds
  184.     local timeFromLastUpdate = os.clock() -- the time frome last update
  185.     local avgUpdateSpeed = 0 -- this indicates the average update speed in seconds per frame
  186.     local updateCount = 0 -- keep track of the frames drawn between each game tick
  187.  
  188.     while true do
  189.         local currentTime = os.clock() -- the current time
  190.         if (currentTime <= timeFromLastUpdate) then
  191.             updateGame(avgUpdateSpeed) -- still the same game tick as the last frame, so just update it with the average delay
  192.             inputPlayer(avgUpdateSpeed) -- same for the player input
  193.  
  194.             updateCount = updateCount + 1 -- increase the update counter
  195.             if (updateCount >= 3) then
  196.                 sleep(0)
  197.             end
  198.         else -- if the next game tick is here:
  199.             local timeOff = -avgUpdateSpeed * (updateCount - 1)
  200.  
  201.             updateGame(currentTime - timeFromLastUpdate + timeOff) -- update the game normally with the difference in time minus the time we estimated wrongly to compensate
  202.             inputPlayer(currentTime - timeFromLastUpdate + timeOff) -- same for the player input
  203.  
  204.             avgUpdateSpeed = 0.05 / (updateCount + 2) -- calculate the new average delay between each frame
  205.  
  206.             updateCount = 1 -- set the update counter to 0 to restart counting
  207.             timeFromLastUpdate = currentTime -- update the time since the last update
  208.         end
  209.  
  210.         coroutine.yield() -- to prevent "too long without yielding" errors without slowing down the program
  211.     end
  212. end
  213.  
  214. parallel.waitForAll(keyInput, gameUpdate, rendering) -- handle input, the game mechanics and the rendering simultaniously
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement