Last active
November 18, 2022 15:59
-
-
Save Quenty/803e0a6f36804e327c60347e52803359 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
-- @classmod CameraChallenge | |
-- @author Quenty | |
local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore")) | |
-- Challenge: | |
-- 1. Make `camera' always point to the the current or last known camera. | |
-- a. Problem: CurrentCamera can be nil. | |
-- b. Guarantee that `camera' is never nil. | |
-- 2. Make `Goodviewport' always be the current actual size of the Goodviewport. | |
-- a. Problem: ViewportSize can falsely report (1,1) before it gets fixed up by RenderPrepare. | |
-- b. Guarantee that `Goodviewport' is never (1,1). | |
-- 3. Yield until the above guarantees are met. | |
-- 4. Don't leak memory. | |
-- See: https://gist.github.com/Fraktality/31bc9cb5abf2af4340cd3a6a775c616d | |
local Workspace = game:GetService("Workspace") | |
local BaseObject = require("BaseObject") | |
local Maid = require("Maid") | |
local Promise = require("Promise") | |
local ValueObject = require("ValueObject") | |
local CameraChallenge = setmetatable({}, BaseObject) | |
CameraChallenge.ClassName = "CameraChallenge" | |
CameraChallenge.__index = CameraChallenge | |
function CameraChallenge.new() | |
local self = setmetatable(BaseObject.new(), CameraChallenge) | |
self.LastCamera = ValueObject.new() | |
self._maid:GiveTask(self.LastCamera) | |
self.GoodViewport = ValueObject.new() | |
self._maid:GiveTask(self.GoodViewport) | |
self._maid:GiveTask(Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function() | |
self.LastCamera.Value = Workspace.CurrentCamera | |
end)) | |
self._maid:GiveTask(self.LastCamera.Changed:Connect(function(...) | |
self:_handleCameraChanged(...) | |
end)) | |
self.LastCamera.Value = Workspace.CurrentCamera | |
return self | |
end | |
function CameraChallenge:PromiseValidViewport() | |
if self:HasValidViewport() then | |
return Promise.resolved() | |
end | |
if self._maid._currentPromise then | |
return self._maid._currentPromise | |
end | |
local maid = Maid.new() | |
local promise = Promise.new(function(resolve, reject) | |
maid:GiveTask(self.LastCamera.Changed:Connect(function() | |
if self:HasValidViewport() then | |
resolve(self.LastCamera.Value, self.GoodViewport.Value) | |
end | |
end)) | |
maid:GiveTask(self.GoodViewport.Changed:Connect(function() | |
if self:HasValidViewport() then | |
resolve(self.LastCamera.Value, self.GoodViewport.Value) | |
end | |
end)) | |
maid:GiveTask(reject) | |
end) | |
self._maid._currentPromise = promise | |
promise:Finally(function() | |
self._maid._currentPromise = nil | |
maid:DoCleaning() | |
end) | |
return promise | |
end | |
function CameraChallenge:HasValidViewport() | |
return self.LastCamera.Value and self.GoodViewport.Value | |
end | |
function CameraChallenge:_handleCameraChanged(newCamera, oldCamera, maid) | |
if not newCamera then | |
self.GoodViewport.Value = nil | |
return | |
end | |
maid:GiveTask(newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function() | |
self:_updateViewport(newCamera) | |
end)) | |
self:_updateViewport(newCamera) | |
end | |
function CameraChallenge:_updateViewport(camera) | |
if camera.ViewportSize == Vector2.new(1, 1) then | |
self.GoodViewport.Value = nil | |
else | |
self.GoodViewport.Value = camera.ViewportSize | |
end | |
end | |
return CameraChallenge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Challenge: | |
-- 1. Make `camera' always point to the the current or last known camera. | |
-- a. Problem: CurrentCamera can be nil. | |
-- b. Guarantee that `camera' is never nil. | |
-- 2. Make `Goodviewport' always be the current actual size of the Goodviewport. | |
-- a. Problem: ViewportSize can falsely report (1,1) before it gets fixed up by RenderPrepare. | |
-- b. Guarantee that `Goodviewport' is never (1,1). | |
-- 3. Yield until the above guarantees are met. | |
-- 4. Don't leak memory. | |
-- See: https://gist.github.com/Fraktality/31bc9cb5abf2af4340cd3a6a775c616d | |
local Rx = require("Rx") | |
local RxInstanceUtils = require("RxInstanceUtils") | |
local function observeChallenge() | |
return RxInstanceUtils.observeProperty(workspace, "CurrentCamera"):Pipe({ | |
Rx.where(function(value) | |
return value ~= nil | |
end); | |
Rx.switchMap(function(camera) | |
return RxInstanceUtils.observeProperty(camera, "ViewportSize"):Pipe({ | |
Rx.where(function(viewportSize) | |
return viewportSize.x ~= 1 and viewportSize.y ~= 1 | |
end); | |
Rx.map(function(viewport) | |
return camera, viewport | |
end) | |
}) | |
end); | |
}) | |
end | |
observeChallenge():Subscribe(function(camera, viewport) | |
-- this code runs once all conditions are met, and always a valid viewport and camera will be here. | |
-- note that the camera may be removed/unparented, and we will not emit a new camera and viewport | |
-- until the next valid viewport and camera are reported. | |
-- this matches the expectations provided above (i.e. the last known/current valid camera) | |
print(camera, viewport) | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment