I am using the 2.1.1 Windows Lua SDK using Vscode. I am very new to programming in general and still trying to understand tables. I am making a top-down car racer and trying to make drifting. To do this I am trying to average the angle of angle of my playerSprite.
I need to average the angle values to have a delay effect as if you were actually experiencing under and oversteering (kinda).
I am doing this by creating a table (averageAngle) and then setting a variable (valueToReplace) to the angle. I then find the spot I want to replace (curentSpot). I then replace the selected spot with the value. This has been working great.
My table averaging code is this:
----------- this is called at the top of the file.
currentSpot = 1
averageAngle = {}
numSpots = 20 -- sets the number of spots
for i = 1, numSpots do -- replaces ever spot with angle
averageAngle[i] = angle
end
----------- this is called in the update function
sumAngle = 0 --VERY IMPORTANT
local valueToReplace = angle -- sets what is replacing things
if currentSpot >= 2 then -- calculates curent spot
currentSpot -= 1
else
currentSpot = numSpots
end
averageAngle[currentSpot] = valueToReplace -- replaces curent spot
for i = 1, numSpots do
sumAngle += averageAngle[i]
end
-- funky way of averaging the table -- needs to be same length as the table
avAngle = (sumAngle) / numSpots
This works great for averaging the actual angles but transitions in and out of drifting and quickly changing from actual rotation to averaged rotation can be quite choppy and can result in the car from "blinking".
Crucnchy
The actual problem.
To solve this I have implemented a complex system of fading and extra tables. Is this the most efficient way of doing this? I hate how I transition out of drifting with the angleAverage2 and notDrifting functions. Is there a better way to do this?
My full Control.lua code:
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"
local pd <const> = playdate
local gfx <const> = pd.graphics
dx = 0 -- CAN NOT BE LOCAL
angle = -90 -- CAN NOT BE LOCAL
local driftingLeniency = 30
-- one time setup for the drifting average table
local currentSpot = 1
averageAngle = {}
local numSpots = 20 -- sets the number of spots
for i = 1, numSpots do -- replaces ever spot with angle
averageAngle[i] = angle
end
-- one time setup for the not drifting average table -- YES BOTH ARE NESSARY
averageAngle2 = {}
local numSpots2 = (driftingLeniency) / 5 -- sets the number of spots -- if driftingLeniency is higher then so is the numSpots
local spotNumber = 1
for i = 1, numSpots2 do -- replaces ever spot with angle
averageAngle2[i] = angle
end
local avAngle = angle -- declare variables to start
local sumAngle = angle
local sumAngle2 = angle
local visableAngle = angle
local oneTime = true
local oneTime2 = true
local drifting = false
------------------------------------------------------------------------------------------------ controlls the rotation of playerSprite
function rotation()
if controllsWanted == 1 then -- checks the wanted controlls
if pd.buttonIsPressed(pd.kButtonLeft) then -- actual change of angle use LEFT and RIGHT
angle -= 5
end
if pd.buttonIsPressed(pd.kButtonRight) then
angle += 5
end
else -- if controllsWanted == 2
local change = playdate.getCrankChange() -- asctual change using the CRANK using the change
angle += (change) -- add it to the angle
end
if angleEasing == true and controllsWanted == 1 then
playerSprite:setRotation( visableAngle ) -- set rotation using method
else
playerSprite:setRotation( angle ) -- set rotation using method
end
end
------------------------------------------------------------------------------------------------ average the angle
function angleAverage()
sumAngle = 0 --VERY IMPORTANT
local valueToReplace = angle -- sets what is replacing things
if currentSpot >= 2 then -- calculates curent spot
currentSpot -= 1
else
currentSpot = numSpots
end
averageAngle[currentSpot] = valueToReplace -- replaces curent spot
for i = 1, numSpots do
sumAngle += averageAngle[i]
end
-- funky way of averaging the table -- needs to be same length as the table
avAngle = (sumAngle) / numSpots
end
function angleAverage2()
sumAngle2 = 0
for i = 1, spotNumber do -- replaces ever spot with angle
averageAngle2[i] = angle
end
for i = 1, numSpots2 do
sumAngle2 += averageAngle2[i]
end
-- funky way of averaging the table -- needs to be same length as the table
visableAngle = (sumAngle2) / numSpots2
end
function weDrifting()
local turnDifference = math.abs(angle - avAngle)
angleEasing = false
if oneTime == true then -- when you fist start drifting replace all angles
for i = 1, numSpots do
averageAngle[i] = angle
end
oneTime = false -- reset variables
oneTime2 = true
drifting = true
spotNumber = 0
pd.timer.performAfterDelay(1000, function () -- waight for one second to then be able to end a drift
driftingCanStop = true
end )
end
if turnDifference <= driftingLeniency and driftingCanStop == true then -- drifting leniency checks if tires could regain traction
drifting = false
end
angleAverage() -- calculate curent average
end
function notDrifting()
drifting = false
if oneTime2 == true then -- when you fist start drifting replace all angles
for i = 1, numSpots2 do
averageAngle2[i] = angle
end
angle = avAngle -- reset variables
oneTime = true
oneTime2 = false
angleEasing = true
driftingCanStop = false
end
if spotNumber < numSpots2 and driftingLeniency > 5 then -- calculates curent spot
spotNumber += 1
angleAverage2() -- calculate curent average
else
angleEasing = false -- when done make shure to switch away just for simplicity
end
end
------------------------------------------------------------------------------------------------ movement of playerSprite
function movement()
local brake = .1 -- basic variables for movement
local acceleration = .4
local deceleration = .4
local maximumSpeed = 12
local turnDifference = math.abs(angle - avAngle)
if controllsWanted == 1 then
if pd.buttonIsPressed(pd.kButtonB) then
dx = math.min(dx + acceleration - offTrack, maximumSpeed) -- when B is pressed then accelerate
else
dx = math.max(dx - deceleration - offTrack, 0) -- when B is not pressed decelerat
end
if pd.buttonIsPressed(pd.kButtonA) then
dx = math.max(dx - brake - offTrack, 0) -- when A is pressed brake
end
if turnDifference >= 55 - dx or drifting == true then -- check if "drifting"
weDrifting()
local moveToX = playerSprite.x + math.cos(math.rad(avAngle)) * dx
local moveToY = playerSprite.y + math.sin(math.rad(avAngle)) * dx
playerSprite:moveTo(moveToX, moveToY)
else
notDrifting()
local moveToX = playerSprite.x + math.cos(math.rad(angle)) * dx
local moveToY = playerSprite.y + math.sin(math.rad(angle)) * dx
playerSprite:moveTo(moveToX, moveToY)
end
else -- if controllsWanted == 2 then -- crank controlls
local change = pd.getCrankChange()
local turnSpeed = math.min(math.abs(change/6), (dx * .1))
if pd.buttonIsPressed(pd.kButtonB) then
dx = math.min(dx + acceleration - turnSpeed - offTrack, maximumSpeed)
else
dx = math.max(dx - deceleration - turnSpeed - offTrack, 0)
end
if pd.buttonIsPressed(pd.kButtonA) then
dx = math.max(dx - brake - turnSpeed - offTrack, 0)
end
local moveToX = playerSprite.x + math.cos(math.rad(angle)) * dx
local moveToY = playerSprite.y + math.sin(math.rad(angle)) * dx
playerSprite:moveTo(moveToX, moveToY)
end
end
Here are some things things I found. The smoothing coming out of a drift needs to be short enough that the player can't initiate another. The smooth effect does not affect the actual direction which is why checking if you have corrected the drift is important to make the player feel like they are actually in control.
Having a higher leniency can actually improve the smoothness is some cases.
Crucnchy
driftingLeniency = 5
driftingLeniency = 30
It obviously has its trade-offs. and is a lot more noticeable when you are controlling.
Final bits.
The game is super bare-bones, on a test track, and a hellscape of variables and if statements.
I have tried to document my code to the best of my ability but if you need any clarification please ask! I would love feedback on the code and on any changes you would make. I have tried a lot of different methods. This is probably the 5th version of drifting.