Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- | Orbit (Interactive Celestial Body Simulator Program) Complete Release v1.2.3 [5/8/2024], written by 1m1m0 & several AI models | --
- -- (Quick Settings/Variables) --
- local scale = math.random(0.8, 1.5) -- Factor to propotionally resize objects, size and mass included
- local trailLifeTime = 1 -- How long a trail will last (seconds)
- local trailSize = 0.25 -- How wide trails will be proportional to their parent circle's radius
- local trailOpaqueness = 0.5 -- How see-through trails will be
- local trailGradient = 0 -- Determines if trails fade out or not
- local numRandomCircles = math.random(2, 75) -- Amount of circles can randomly spawn
- local circleMass = math.random(1, 12) -- Mass of circles while being randomized
- local radiusMax = math.random(15, 25) -- Maximum random radius of circles while being randomized
- local radiusMin = math.random(8, 13) -- Minimum random radius of circles while being randomized
- local throwAmplifier = 0.075 -- Factor in which how fast an object will be after throwing or flicking it
- local velocityRate = 1 -- Time (seconds) to calculate the velocity of the circle while thrown (pix/s)
- local dampingAmplifier = 0.25 -- Factor to simulate drag or air resistance (set to 0 for cosmic simulations)
- local randomCirclesBounce = math.random(0.0, 0.535) -- Elasticity of the Randomized Circles
- local wrapBuffer = 0.5 -- Ensures the circle is fully off-screen to wrap around the screen
- -- (Togglables) --
- local toggleWalls = true -- Toggle to have bounderies to the simulation
- local toggleBodyWrap = true -- Toggle teleporting objects across the screen when crossing the walls
- local toggleStartingCircle = true -- Toggle to spawn a starting circle
- local toggleCustomCircles = true -- Toggle to spawn more starting circles which can be changed in 'local customCircles = 4'
- local toggleRandomCircles = true -- Toggle randomized circles
- local toggleTrails = true -- Toggle trails on/off
- local toggleForces = true -- Toggle gravitational forces on the circles
- local toggleGravity = false -- Toggle gravity of the simulation
- -- (Do not Touch) --
- local screenW = display.contentWidth
- local screenH = display.contentHeight
- -- (Starting Circle quick settings) --
- local customCircles = 1 -- Number of custom starting circles to spawn
- local startingCircleBounce = 0.25 -- Elasticity of the circle
- local startingCircleXpos = screenW/2 -- Center of screen
- local startingCircleYpos = screenH/2
- local startingCircleRadius = 50 -- Size of circle
- local startingCircleMass = 500 -- Mass of circle
- local startingColorR = 255 -- Red value (Maximum 255)
- local startingColorG = 255 -- Green value (Maximum 255)
- local startingColorB = 255 -- Blue value (Maximum 255)
- ----> | Main Program | <----
- -- Initialize physics
- local physics = require("physics")
- physics.start()
- if toggleGravity == false then
- physics.setGravity(0, 0) -- Set gravity to 0
- end
- local spheres = {} -- Table to keep track of spheres
- -- [[ Starting Circle with fixed size & mass for experimentations
- local function createCircle(x, y, radius, mass, r, g, b)
- local circle = display.newCircle(x, y, radius)
- -- Prevent color errors
- if startingColorR <= 0 or startingColorR >= 255 then
- startingColorR = 1
- elseif startingColorG <= 0 or startingColorG >= 255 then
- startingColorG = 1
- elseif startingColorB <= 0 or startingColorB >= 255 then
- startingColorB = 1
- end
- circle:setFillColor(startingColorR, startingColorG, startingColorB)
- physics.addBody(circle, {radius=radius, density=mass/(math.pi*radius*radius), bounce=startingCircleBounce})
- circle.mass = mass
- table.insert(spheres, circle)
- -- Combined function with improved throwing and double-click locking mechanism
- function circle:touch(event)
- if event.phase == "began" then
- -- Initial touch
- display.getCurrentStage():setFocus(self, event.id)
- self.isFocus = true
- self.markX = self.x
- self.markY = self.y
- self.startTime = system.getTimer()
- self.movementHistory = {}
- -- Stop the sphere's movement (only if not locked)
- if not self.isLocked then
- self:setLinearVelocity(0, 0)
- self.angularVelocity = 0
- end
- elseif self.isFocus then
- if event.phase == "moved" then
- -- Dragging (only if not locked)
- if not self.isLocked then
- self.x = event.x - event.xStart + self.markX
- self.y = event.y - event.yStart + self.markY
- -- Record movement history
- table.insert(self.movementHistory, {time = system.getTimer(), x = event.x, y = event.y})
- -- Clean up old movement history
- local currentTime = system.getTimer()
- local cutOffTime = currentTime - (velocityRate*1000)
- for i, record in ipairs(self.movementHistory) do
- if record.time < cutOffTime then
- table.remove(self.movementHistory, i)
- end
- end
- end
- elseif event.phase == "ended" or event.phase == "cancelled" then
- if not self.isLocked then -- Don't apply a throw if locked
- -- Released
- local endTime = system.getTimer()
- local distance = 0
- local throwForce = 0
- local timeDiff = 0
- if #self.movementHistory >= 2 then
- local lastRecord = self.movementHistory[#self.movementHistory]
- local prevRecord = self.movementHistory[#self.movementHistory - 1]
- distance = math.sqrt((lastRecord.x - prevRecord.x) ^ 2 + (lastRecord.y - prevRecord.y) ^ 2)
- timeDiff = lastRecord.time - prevRecord.time
- throwForce = distance * throwAmplifier / timeDiff
- -- Apply decay to the throw force to simulate energy loss
- throwForce = throwForce * math.exp(-timeDiff * dampingAmplifier)
- local vx = (event.x - self.markX) * throwForce
- local vy = (event.y - self.markY) * throwForce
- self:applyLinearImpulse(vx, vy, self.x, self.y)
- end
- end
- display.getCurrentStage():setFocus(self, nil)
- self.isFocus = false
- end
- elseif event.phase == "ended" and not self.isFocus then
- -- Detect double click
- if self.lastClickTime and (system.getTimer() - self.lastClickTime) < 300 then
- -- Double click detected within 300 milliseconds
- self.isLocked = not self.isLocked
- self.body:setType(self.isLocked and "static" or "dynamic")
- self.lastClickTime = nil
- else
- -- Single click, start timer
- self.lastClickTime = system.getTimer()
- end
- end
- return true
- end
- circle:addEventListener("touch", circle)
- return circle
- end
- --]]
- -- Function to spawn multiple starting circles
- local function spawnStartingCircles()
- if customCircles <= 0 then -- Prevents negative starting circles
- customCircles = 1
- end
- if startingCircleRadius <= 0 then -- Prevents negative radius
- startingCircleRadius = 25
- end
- for i = 1, (customCircles-1) do
- local xPosition = math.random(startingCircleRadius, screenW - startingCircleRadius)
- local yPosition = math.random(startingCircleRadius, screenH - startingCircleRadius)
- createCircle(xPosition, yPosition, startingCircleRadius, startingCircleMass)
- end
- end
- -- Function to create a new sphere with a random color and trail
- local function createSphere(x, y, radius, mass)
- -- Random color components
- local r = math.random()
- local g = math.random()
- local b = math.random()
- local sphere = display.newCircle(x, y, radius)
- sphere:setFillColor(r, g, b) -- Set the random color
- physics.addBody(sphere, {radius=radius, density=mass/(math.pi*radius*radius), bounce=randomCirclesBounce})
- sphere.mass = mass
- table.insert(spheres, sphere)
- -- [[ Trail effect
- if toggleTrails == true then
- local trailGroup = display.newGroup()
- function sphere:enterFrame(event)
- local trail = display.newCircle(trailGroup, self.x, self.y, radius * trailSize)
- trail:setFillColor(r, g, b, trailOpaqueness) -- Slightly transparent version of the sphere's color
- transition.to(trail, {time=(trailLifeTime*1000), alpha=trailGradient, onComplete=function() display.remove(trail) end})
- end
- elseif toggleTrails == false then
- trailGradient = 1
- trailLifeTime = 0
- trailOpaqueness = 0
- trailSize = 0
- end
- --]]
- Runtime:addEventListener("enterFrame", sphere)
- -- Combined function with improved throwing and double-click locking mechanism
- function sphere:touch(event)
- if event.phase == "began" then
- -- Initial touch
- display.getCurrentStage():setFocus(self, event.id)
- self.isFocus = true
- self.markX = self.x
- self.markY = self.y
- self.startTime = system.getTimer()
- self.movementHistory = {}
- -- Stop the sphere's movement (only if not locked)
- if not self.isLocked then
- self:setLinearVelocity(0, 0)
- self.angularVelocity = 0
- end
- elseif self.isFocus then
- if event.phase == "moved" then
- -- Dragging (only if not locked)
- if not self.isLocked then
- self.x = event.x - event.xStart + self.markX
- self.y = event.y - event.yStart + self.markY
- -- Record movement history
- table.insert(self.movementHistory, {time = system.getTimer(), x = event.x, y = event.y})
- -- Clean up old movement history
- local currentTime = system.getTimer()
- local cutOffTime = currentTime - (velocityRate*1000)
- for i, record in ipairs(self.movementHistory) do
- if record.time < cutOffTime then
- table.remove(self.movementHistory, i)
- end
- end
- end
- elseif event.phase == "ended" or event.phase == "cancelled" then
- if not self.isLocked then -- Don't apply a throw if locked
- -- Released
- local endTime = system.getTimer()
- local distance = 0
- local throwForce = 0
- local timeDiff = 0
- if #self.movementHistory >= 2 then
- local lastRecord = self.movementHistory[#self.movementHistory]
- local prevRecord = self.movementHistory[#self.movementHistory - 1]
- distance = math.sqrt((lastRecord.x - prevRecord.x) ^ 2 + (lastRecord.y - prevRecord.y) ^ 2)
- timeDiff = lastRecord.time - prevRecord.time
- throwForce = distance * throwAmplifier / timeDiff
- -- Apply decay to the throw force to simulate energy loss
- throwForce = throwForce * math.exp(-timeDiff * dampingAmplifier)
- local vx = (event.x - self.markX) * throwForce
- local vy = (event.y - self.markY) * throwForce
- self:applyLinearImpulse(vx, vy, self.x, self.y)
- end
- end
- display.getCurrentStage():setFocus(self, nil)
- self.isFocus = false
- end
- elseif event.phase == "ended" and not self.isFocus then
- -- Detect double click
- if self.lastClickTime and (system.getTimer() - self.lastClickTime) < 300 then
- -- Double click detected within 300 milliseconds
- self.isLocked = not self.isLocked
- self.body:setType(self.isLocked and "static" or "dynamic")
- self.lastClickTime = nil
- else
- -- Single click, start timer
- self.lastClickTime = system.getTimer()
- end
- end
- return true
- end
- sphere:addEventListener("touch", sphere)
- return sphere
- end
- -- [[ Function to update gravitational forces
- local function updateGravity()
- for i = 1, #spheres do
- for j = i+1, #spheres do
- local sphere1 = spheres[i]
- local sphere2 = spheres[j]
- local dx = sphere2.x - sphere1.x
- local dy = sphere2.y - sphere1.y
- local distance = math.sqrt(dx*dx + dy*dy)
- local force = (sphere1.mass * sphere2.mass) / (distance * distance)
- local forceX = force * (dx / distance)
- local forceY = force * (dy / distance)
- sphere1:applyForce(forceX, forceY, sphere1.x, sphere1.y)
- sphere2:applyForce(-forceX, -forceY, sphere2.x, sphere2.y)
- end
- end
- end
- --]]
- -- Create screen boundaries that fit any screen size
- local function createWalls()
- local leftWall = display.newRect(display.screenOriginX, display.contentCenterY, 1, display.actualContentHeight)
- local rightWall = display.newRect(display.actualContentWidth + display.screenOriginX, display.contentCenterY, 1, display.actualContentHeight)
- local topWall = display.newRect(display.contentCenterX, display.screenOriginY, display.actualContentWidth, 1)
- local bottomWall = display.newRect(display.contentCenterX, display.actualContentHeight + display.screenOriginY, display.actualContentWidth, 1)
- physics.addBody(leftWall, "static")
- physics.addBody(rightWall, "static")
- physics.addBody(topWall, "static")
- physics.addBody(bottomWall, "static")
- end
- -- Function to create a Portal-like wrap-around effect
- local function wrapAroundScreen(object)
- local buffer = object.width * wrapBuffer -- Buffer to ensure the circle is fully off-screen before wrapping
- local leftBoundary = display.screenOriginX
- local rightBoundary = display.actualContentWidth + display.screenOriginX
- local topBoundary = display.screenOriginY
- local bottomBoundary = display.actualContentHeight + display.screenOriginY
- -- Check if any part of the object is beyond the screen boundary
- if object.x - buffer > rightBoundary then
- object.x = leftBoundary - buffer + (object.x - buffer - rightBoundary)
- elseif object.x + buffer < leftBoundary then
- object.x = rightBoundary + buffer - (leftBoundary - (object.x + buffer))
- end
- if object.y - buffer > bottomBoundary then
- object.y = topBoundary - buffer + (object.y - buffer - bottomBoundary)
- elseif object.y + buffer < topBoundary then
- object.y = bottomBoundary + buffer - (topBoundary - (object.y + buffer))
- end
- end
- -- Background touch listener for panning
- local function backgroundTouch(event)
- local background = event.target
- if event.phase == "began" then
- -- Double-click detection
- if background.lastClickTime and (system.getTimer() - background.lastClickTime) < 300 then
- -- Begin panning
- background.isPanning = true
- background.startX = event.x - background.x
- background.startY = event.y - background.y
- end
- background.lastClickTime = system.getTimer()
- elseif event.phase == "moved" then
- if background.isPanning then
- -- Perform panning
- background.x = event.x - background.startX
- background.y = event.y - background.startY
- end
- elseif event.phase == "ended" or event.phase == "cancelled" then
- background.isPanning = false
- end
- return true
- end
- -- Modify the enterFrame event to include the Portal-like wrap-around logic
- local function onEnterFrame(event)
- for _, sphere in ipairs(spheres) do
- wrapAroundScreen(sphere)
- end
- --[[
- updateGravity() -- Keep the original gravity update logic if needed
- --]]
- end
- -- Function to generate a random number of circles with random properties
- local function generateRandomCircles()
- -- Prevent weird math from occuring
- if radiusMin <= 0 then
- radiusMin = 10
- elseif radiusMax <= radiusMin or radiusMax <= 0 then
- radiusMax = 20
- end
- local numCircles = numRandomCircles -- Random number of circles
- for i = 1, numCircles do
- local radius = (math.random(radiusMin, radiusMax))*scale -- Random radius
- local mass = (radius * circleMass)*scale -- Mass proportional to radius, adjust as needed
- local x = math.random(radius, screenW - radius) -- Random x position, ensuring circle stays on screen
- local y = math.random(radius, screenH - radius) -- Random y position, ensuring circle stays on screen
- createSphere(x, y, radius, mass)
- end
- end
- -- Togglable Functions
- -- Call the function to generate random circles
- if toggleRandomCircles == true then
- generateRandomCircles()
- end
- -- Create walls
- if toggleWalls == true then
- createWalls() -- Disable for portal-like wrapping
- end
- -- Create starting circle
- if toggleStartingCircle == true then
- createCircle(startingCircleXpos, startingCircleYpos, startingCircleRadius, startingCircleMass)
- end
- -- Call the function to spawn the starting circles
- if toggleCustomCircles == true then
- spawnStartingCircles()
- end
- -- Update gravity every frame
- if toggleForces == true then
- Runtime:addEventListener("enterFrame", updateGravity)
- end
- -- [[ Object wrapping (Infinite space)
- if toggleBodyWrap == true then
- Runtime:addEventListener("enterFrame", onEnterFrame)
- end
- ----< | End of Main Program | >----
- print("Orbit Beta v1.2.3 May 8 2024 Edition has initialized. Updates are available on the Pastbin: https://pastebin.com/qixBZPLH")
- -- [ This code primarily uses Lua and runs on Solar2D or Corona Simulator 2024. This program is not yet an application and is still in prototype stages. Updates will occasionally be made with additional features and debugging. ] --
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement