PlayHereMiddleman - 'Play Here' alternative for custom character situations

Studio’s “Play Here” test mode does not work if you’re using a custom character solution, and there is no built-in way to retrieve the studio’s ‘play here’ location.

This is something I’ve dealt with for the longest time and haven’t really considered how disappointing it is until now. I created this plugin as an workaround until an alternative is officially implemented (support the feature request here)


Get it here:


How to enable for play tests

  1. With the plugin installed, simply toggle the plugin button on:

image

Setting up your scripts

  1. Now, in your script that handles spawning the player’s character, you can check if you’re looking to ‘play here’ during play tests

    • An attribute is being constantly set under Players called StudioCameraCFrame
    • If the attribute is nil, the plugin button is deactivated
-- Example usage
-- This example is a bit arbitrary, mind you, because this is meant for scenarios where your character spawning logic is custom

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

local function GetPlayHereLocation(): CFrame?
	if not RunService:IsStudio() then
		return nil
	end
	
	return Players:GetAttribute("StudioCameraCFrame") :: CFrame?
end

local function OnCharacterAdded(Model: Model)
	local RootPart = Model.PrimaryPart :: BasePart	
	local PlayHereLocation = GetPlayHereLocation()
	
	if PlayHereLocation then
		print("User wants to 'play here' at CFrame:", PlayHereLocation)		
		RootPart.CFrame = PlayHereLocation
	else
		print("User did not want to 'play here'! Spawning at default location...")		
		RootPart.CFrame = CFrame.new(0, 10, 0)
	end
end

if Player.Character then
	task.spawn(OnCharacterAdded, Player.Character)
end
Player.CharacterAdded:Connect(OnCharacterAdded)

Saves between sessions

If you close studio while the plugin button is enabled, it’ll remember the next time you open studio. If you’re not fond of this please let me know and I’ll make it an option


The code is pretty simple (~100 lines of code). Feel free to do anything you’d like with it

Plugin code

-- Written by @baseparts
--!strict

local ATTRIBUTE_NAME = "StudioCameraCFrame"
local UPDATE_RATE = (1/5)
local SET_ON_INSTANCE = game:GetService("Players")
local DEBUG = false

local RunService = game:GetService("RunService")

-- Ensure in studio, not playtesting
if not RunService:IsEdit() then
	return
end

local Toolbar = plugin:CreateToolbar("PlayHere")
local Button = Toolbar:CreateButton("Toggle Play Here Middleman", "Play Here Middleman", "rbxassetid://81018894975829")
Button.ClickableWhenViewportHidden = true

local IsEnabled = false
local EnabledConnection: RBXScriptConnection?

---------------------------------------------------

local function DebugPrint(...)
	if DEBUG then
		print(...)
	end
end


local function Update()
	local Camera = workspace:FindFirstChildOfClass("Camera")
	
	if not Camera then
		return
	end
	
	SET_ON_INSTANCE:SetAttribute(ATTRIBUTE_NAME, Camera.CFrame)
end


local function Disable()
	if not IsEnabled then
		return
	end

	IsEnabled = false

	if EnabledConnection then
		EnabledConnection:Disconnect()
		EnabledConnection = nil
	end
	
	SET_ON_INSTANCE:SetAttribute(ATTRIBUTE_NAME, nil)
	DebugPrint("Disabled PHM")
end


local function Enable()
	if IsEnabled then
		return
	end
	
	IsEnabled = true
	
	-- New connection
	local _rateLimitPool = 0
	EnabledConnection = RunService.Heartbeat:Connect(function(DeltaTime: number)
		-- Rate Limit
		_rateLimitPool += DeltaTime
		if _rateLimitPool > UPDATE_RATE then
			_rateLimitPool = 0
		else
			return
		end
		
		Update()
	end)
	
	DebugPrint("Enabled PHM")
end

---------------------------------------------------

-- Enable if its found that it was previously enabled
local WasEnabled = (SET_ON_INSTANCE:GetAttribute(ATTRIBUTE_NAME) ~= nil)

if WasEnabled then
	DebugPrint("PHM was found to be previously enabled")
	Button:SetActive(true)
	Enable()
end

-- Plugin button
Button.Click:Connect(function()
	if IsEnabled then
		Button:SetActive(false)
		Disable()
	else
		Button:SetActive(true)
		Enable()
	end
end)

-- Plugin lifecycle
plugin.Unloading:Connect(function()
	Disable()
end)

Let me know your thoughts or suggestions!

6 Likes

Just realized a bug :partying_face: give me a minute Fixed!