Very early preview but thought I’d share it here.
Also, forgot to mention, it has bugs.
1. If you started playing and started using the calculator for first time, it just doesn’t start for example 1, it starts with 11.
2. Instead of NumValue
it uses IntValue
. It doesn’t show decimal numbers.
2.1. The decimal button doesn’t work.
When I’ll return from vacation, I’ll post a footage, because when I made it, I forgot to record it.
Personally, the old UI looked a lot cooler, but it looks really clean, nice job.
Interior Mapping: Fake 3d Interiors with a Flat Image (Devlog P2)
You can view the full topic here: Interior Mapping: Fake 3d Interiors with a Flat Image
I did a couple updates to my interior mapping system: it’s now more accurate, runs faster, and is easier to use.
I suggest checking out the topic above.
In a recent visit to my experience in VR, I found that I moved so slowly that I got stuck under the counter door in my still-incomplete restaurant. It’s nice that I don’t get nausea easily while I’m in VR (unless I drive a vehicle around for some reason), but some other people aren’t as lucky as I am in this regard.
I knew I needed to fix this somehow. As of today, I might have¹ done it, with a new function in “CharacterFunctions”, IsCharacterNear(). This function iterates through every player on the server, tries to find their character a couple times (not using :Wait() to avoid yielding the calling script), then checks if they’re near specific coordinates using a magnitude comparison.
Until this function returns false, the flap won’t lower back into its usual position. Check out this video to see how it looks! (Please ignore how the restaurant hasn’t changed one bit in possibly a year at this point.)
Also, yes, since my experience tries to treat NPCs and players equally, the door won’t close if a tracked NPC is standing near it.
I know some of you might be thinking of the meme, so yes… That woman is just standing there…MENANCINGLY!
Source code
Just because I think it could be useful to other developers, here’s the function, copied from my ModuleScript directly. You’ll have to remove the “CharacterStorage:” prefix on the function’s name, or change it to whatever your ModuleScript identifies itself as in itself:
-- CharacterFunctions:IsCharacterNear(takes Vector3 and number, returns boolean)
-- Compares all players (and NPC characters that have opted in)'s positions to
-- the provided reference coordinates, and returns TRUE if any of them are within
-- a specific number of studs to it. This function assumes that a player isn't
-- near the point of interest if it can't get their character reference within
-- a specific number of attempts, so this may fail and cause VR players to get
-- stuck, which could lead to nausea.
-- This function is meant to run on the server, but can run on the client. As
-- NPCs are planned to be handled by the server, any client-side scripts will
-- need to be aware of any NPCs that need to be checked for, so doors can't
-- close near them and other examples. For that, a RemoteEvent might be needed
-- so clients know when to add an NPC to their local list, which isn't synced.
local NPCScanList = {}
-- TODO: Consider changing this function to use CharacterStorage entries; All NPCs and players must generate them after spawning, and they contain
-- references to things like their HumanoidRootPart and Character, which would remove the need for the pcall.
function CharacterFunctions:IsCharacterNear(_reference : Vector3, _radius : number)
-- Longer-term variables
local IsClose = false -- This will be set to TRUE if any character is too close to this door.
-- Temporary variables
local char : Model = nil -- Temporary variable for each Player's character, nil'd between each iteration.
local HRP : BasePart = nil -- The iterated player's HumanoidRootPart, used for a distance check.
local RemainingTries = 0
local success = false -- This is set to true if a Player's character is found inside of the pcall.
-- First scan for any nearby players.
for _, player in Players:GetPlayers() do
RemainingTries = 5
while RemainingTries > 0 do
success = pcall (function()
char = player.Character
HRP = char:FindFirstChild("HumanoidRootPart")
end)
-- Break the loop early if the player's character is found without errors.
if success and char then
RemainingTries = 0
break
end
task.wait(0.075)
end
-- Is this Player within the requested number of studs of the door? They're in the way if that's the case! Break the loop here.
if char and HRP and (HRP.Position-_reference).Magnitude <= _radius then
IsClose = true
break
end
-- Reset the temporariy variables, so each character is checked, with no way to "sneak past" the scan.
char = nil
HRP = nil
success = false
end
-- No players were near the location? If there are any NPCs on the list, see if any of them are close to it.
-- TODO: This part of the function is basically copy-pasted from the player iteration above. Perhaps both could be merged into a single list,
-- then handled based on their type? (Model = NPC, Player = player, get their character)
if not IsClose and #NPCScanList > 0 then
for _, npc : Model in NPCScanList do
RemainingTries = 5
while RemainingTries > 0 do
success = pcall (function()
HRP = npc:FindFirstChild("HumanoidRootPart")
end)
-- Break the loop early if their HumanoidRootPart is found without errors.
if success and HRP then
RemainingTries = 0
break
end
task.wait(0.075)
end
-- Is this NPC within the requested number of studs of the door? They're in the way if that's the case! Break the loop here.
if HRP and (HRP.Position-_reference).Magnitude <= _radius then
IsClose = true
break
end
-- Reset the temporariy variables, so each character is checked, with no way to "sneak past" the scan.
char = nil
HRP = nil
success = false
end
end
return IsClose
end
As a bonus, you can also see my “TODO” comments, which reference an idea (NPC characters) and CharacterStorage, a system that unifies references to players and NPCs by creating data structures for both that give references to their character models and the like. Since it’s specific to my experience, I’m keeping that private…for now.
Actually, I’ve already updated this to use CharacterStorage, and it’s probably more “performant” now as a result. The older code version here will work in other experiences, though.
¹This function won’t always prevent players from getting stuck beneath the door. If it can’t get a reference to their character model in time, it’ll act as if they aren’t near it!
Still working on learning more about scripting and how I can improve and be a better developer!
Cool! By the way, you don’t have to use pcalls for checking for potentially nil parts:
pcall fixes
RemainingTries = 5
while RemainingTries > 0 do
success = pcall (function()
char = player.Character
HRP = char:FindFirstChild("HumanoidRootPart")
end)
-- Break the loop early if the player's character is found without errors.
if success and char then
RemainingTries = 0
break
end
task.wait(0.075)
end
^^ this can be shortened to vv
while RemainingTries > 0 do
HRP = player and player.Character and player.Character:FindFirstChild("HumanoidRootPart") -- This pretty much means if player, and if player.Character, then if player.Character:FindFirstChild("HumanoidRootPart") then return it
-- Break the loop early if the player's character is found without errors.
if HRP then
RemainingTries = 0
break
end
task.wait(0.075)
end
Same thing with this:
success = pcall (function()
HRP = npc:FindFirstChild("HumanoidRootPart")
end)
can be shortened to:
HRP = npc and npc:FindFirstChild("HumanoidRootPart")
That’s a good tip! What happens if it finds the player’s character but doesn’t find their HumanoidRootPart, though? Does it return the Character or just nil?
I’ve updated the function to use my experience’s CharacterStorage “system”, so that code’s now pretty much completely different now. This advice/tip could be useful for CharacterStorage, though, since that “system” tries to quickly get references to character models, body parts, and other instances many times. I don’t know if that script uses a pcall, though.
-- RemainingTries = 5
if player:GetAttribute("CharacterGUID") then
entry = CharStorage:GetDictionary(player:GetAttribute("CharacterGUID"), false)
if entry then
-- Is this Player within the requested number of studs of the door? They're in the way if that's the case! Break the loop here.
if entry.Character and entry.Humanoid.RootPart and (entry.Humanoid.RootPart.Position-_reference).Magnitude <= _radius then
IsClose = true
break
end
else print("IsCharacterNear:", player.Name, "doesn't have a CharacterStorage entry yet. Skipping!")
end
else print("IsCharacterNear:", player.Name, "hasn't been given a GUID yet. Skipping!")
end
Started my latest project, mostly placeholders, lighting isn’t complete, and missing 70% of the details, but this is it so far!
In the code I provided, it would return nil
You can also do or
, if
, then
, else
, etc. These are called ternary expressions, and are like if statements on one line.
And now onto making rocks painting with my mouse. Anyways, what do you think of the trees? Spent a couple days improving them lots.
That looks great and thanks for mentioning Figma - I had not heard of that and it looks useful!
It is really helpful to make a design for a webpage. It’s free, there are enough tutorials online to watch to understand Figma. You can use it in a browser or just in their desktop app!