Skip to content

Commit 917ead4

Browse files
update Open Source Docs from Roblox internal teams
1 parent 9c27568 commit 917ead4

File tree

6 files changed

+371
-144
lines changed

6 files changed

+371
-144
lines changed
Lines changed: 3 additions & 0 deletions
Loading

content/en-us/production/promotion/rewarded-video-ads.md

Lines changed: 102 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -20,64 +20,102 @@ With rewarded video ads, you can implement a reward mechanism inside your experi
2020
</Alert>
2121

2222
<Alert severity="warning">
23-
The video ad must not impact the user's character inside the experience. For example, you can prevent the user's character from taking damage while the user is watching the ad, or only show the ad in safe zones like the experience lobby.
23+
Video ads shouldn't negatively impact the user's character during gameplay. To make sure their character isn't harmed, you can pause damage while an ad plays or only show ads in safe zones like the experience lobby.
2424
</Alert>
2525

26-
To implement a rewarded video ad, you must set up the video ad inside your experience, create a client-side script that checks if a video ad is available to be played to the user, and then create a server-side script that turns a developer product into a reward, shows the user the video ad, and grants the user their reward.
26+
To implement a rewarded video ad, you must set up the video ad inside your experience and then create client-side and server-side scripts. The client-side script checks if a video ad is available to be played to the user, while the server-side script turns a developer product into a reward, shows the user the video ad, and grants the user their reward.
27+
28+
<Alert severity="info">
29+
Earnings from rewarded video ads come from impressions. Your total earnings are calculated by multiplying EPM (earnings per 1000 impressions) by the total number of impressions.
30+
</Alert>
2731

2832
### Video ad setup
2933

3034
To set up a rewarded video ad inside your experience:
3135

32-
1. Open the experience in Studio and select the location where you want to trigger the video ad.
33-
2. [Insert a click-to-play video ad unit in that location](../monetization/immersive-ads.md#billboards).
34-
3. Go to the **Game Settings** menu and check the **Enable Rewarded Video Ads** checkbox.
35-
4. Select the reward you want to grant the user. If the reward doesn't already exist, [create a new developer product in the Creator Hub](../monetization/developer-products.md#create-a-developer-product).
36-
5. Insert a button that the user must press before the video ad starts playing.
36+
1. In Studio, go to **Game Settings** > **Monetization**.
37+
2. Check the **Enable Rewarded Video Ads** checkbox.
38+
<img src="../../assets/promotion/ads-manager/EnableRewardedVideoAdsToggle.png" width="750" alt="The default Roblox matchmaking flow." />
39+
3. Select the reward you want to grant the user. If the reward doesn't already exist, [create a new developer product in the Creator Hub](../monetization/developer-products.md#create-a-developer-product). This developer product must have been created for the specific universe the place is in.
40+
4. Insert a button that the user must press before the video ad starts playing.
3741

3842
### Client-side implementation
3943

4044
To implement the rewarded video ad on the client-side:
4145

42-
1. Add a new `Class.LocalScript` to the button you just implemented.
46+
1. Add a new `Class.LocalScript` to the button you just inserted into your experience.
4347
2. Use the `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` method to make sure the button is only visible to the user if an ad is available.
4448

4549
```lua title="Code example for rewarded video ad (Client)"
46-
-- Client (LocalScript in a UI button)
47-
local ReplicatedStorage = game:GetService("ReplicatedStorage")
50+
-- Services
4851
local AdService = game:GetService("AdService")
49-
local requestShowAdEvent = ReplicatedStorage:WaitForChild("RequestShowAdEvent")
50-
local recheckAdAvailabilityEvent = ReplicatedStorage:WaitForChild("RecheckAdAvailabilityEvent")
51-
52-
-- Assume the script is a child of the button
53-
local adButton = script.Parent
54-
55-
-- Hide the UI button until the video ad availability is confirmed
56-
adButton.Visible = false
57-
local function checkAdAvailability()
58-
local adAvailability = AdService:GetAdAvailabilityNowAsync(Enum.AdFormat.RewardedVideo)
59-
if adAvailability.AdAvailabilityResult == Enum.AdAvailabilityResult.IsAvailable then
60-
61-
-- If the video ad is available, show the UI button
62-
adButton.Visible = true
63-
else
64-
-- If the video ad is not available, hide the UI button
65-
adButton.Visible = false
66-
-- Recheck ad availability after 60 seconds
67-
task.delay(60, checkAdAvailability)
68-
end
52+
local Players = game:GetService("Players")
53+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
54+
55+
-- StarterGui > ScreenGui > ShopFrame > RewardedAdButton
56+
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
57+
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
58+
local ShopFrame = ScreenGui:WaitForChild("ShopFrame")
59+
local RewardedAdButton = ShopFrame:WaitForChild("RewardedAdButton")
60+
61+
-- StarterGui > ScreenGui > OpenShopButton
62+
local OpenShopButton = ScreenGui:WaitForChild("OpenShopButton")
63+
64+
-- Event to communicate between clients & server
65+
local RewardedAdEvent = ReplicatedStorage:WaitForChild("RewardedAdEvent")
66+
67+
local INELIGIBLE_RESULTS = {
68+
Enum.AdAvailabilityResult.PlayerIneligible,
69+
Enum.AdAvailabilityResult.DeviceIneligible,
70+
Enum.AdAvailabilityResult.PublisherIneligible,
71+
Enum.AdAvailabilityResult.ExperienceIneligible,
72+
}
73+
74+
local function isIneligible(result: Enum.AdAvailabilityResult)
75+
for _, inEligibleResult in ipairs(INELIGIBLE_RESULTS) do
76+
if result == inEligibleResult then
77+
return true
78+
end
79+
end
80+
81+
return false
6982
end
7083

71-
-- Check if the video ad is available
72-
checkAdAvailability()
84+
function checkForAds()
85+
local isSuccess, result = pcall(function()
86+
return AdService:GetAdAvailabilityNowAsync(Enum.AdFormat.RewardedVideo)
87+
end)
88+
89+
90+
if isSuccess and result.AdAvailabilityResult == Enum.AdAvailabilityResult.IsAvailable then
91+
RewardedAdButton.Visible = true
92+
return
93+
end
94+
95+
if isIneligible(result.AdAvailabilityResult) then
96+
return
97+
end
98+
end
7399

74-
-- Use Activated event
75-
adButton.Activated:Connect(function()
76-
requestShowAdEvent:FireServer()
100+
RewardedAdEvent.OnClientEvent:Connect(function(isSuccess : boolean, result : Enum.ShowAdResult)
101+
if result == Enum.ShowAdResult.ShowCompleted then
102+
checkForAds()
103+
end
77104
end)
78105

79-
recheckAdAvailabilityEvent.OnClientEvent:Connect(function()
80-
checkAdAvailability()
106+
OpenShopButton.MouseButton1Click:Connect(function()
107+
if ShopFrame.Visible then
108+
ShopFrame.Visible = false
109+
return
110+
end
111+
112+
ShopFrame.Visible = true
113+
checkForAds()
114+
end)
115+
116+
RewardedAdButton.MouseButton1Click:Connect(function()
117+
RewardedAdButton.Visible = false
118+
RewardedAdEvent:FireServer()
81119
end)
82120
```
83121

@@ -92,55 +130,42 @@ To implement the rewarded video ad on the server-side:
92130
4. Use the `Class.MarketplaceService.ProcessReceipt|ProcessReceipt` method to grant the user their reward if they have watched the entire video ad.
93131

94132
```lua title="Code example for rewarded video ad (Server)"
95-
-- Server (Script in ServerScriptService)
96-
local ReplicatedStorage = game:GetService("ReplicatedStorage")
133+
-- Services
97134
local AdService = game:GetService("AdService")
98135
local MarketplaceService = game:GetService("MarketplaceService")
99136
local Players = game:GetService("Players")
137+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
100138

101-
-- Confirm that the EnableRewardedVideoAds setting is enabled in the experience settings
102-
local requestShowAdEvent = Instance.new("RemoteEvent")
103-
requestShowAdEvent.Name = "RequestShowAdEvent"
104-
requestShowAdEvent.Parent = ReplicatedStorage
105-
local recheckAdAvailabilityEvent = Instance.new("RemoteEvent")
106-
recheckAdAvailabilityEvent.Name = "RecheckAdAvailabilityEvent"
107-
recheckAdAvailabilityEvent.Parent = ReplicatedStorage
139+
local RewardedAdEvent = ReplicatedStorage:WaitForChild("RewardedAdEvent")
108140

109141
-- Provide a developer product ID for the video ad reward
110-
local rewardDevProductId = 12345
111-
local function processReceipt(receiptInfo)
112-
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
113-
if not player then
114-
return Enum.ProductPurchaseDecision.NotProcessedYet
115-
end
116-
if receiptInfo.ProductId == rewardDevProductId then
117-
print(player.Name .. " earned the reward!")
118-
119-
-- Include the logic for granting rewards here
120-
121-
return Enum.ProductPurchaseDecision.PurchaseGranted
122-
end
123-
return Enum.ProductPurchaseDecision.NotProcessedYet
124-
end
142+
-- This developer product must be created for the specific universe that this place is in
143+
local DEV_PRODUCT_ID = 1919753834
144+
145+
RewardedAdEvent.OnServerEvent:Connect(function(player)
146+
local isSuccess, result = pcall(function()
147+
local reward = AdService:CreateAdRewardFromDevProductId(DEV_PRODUCT_ID)
148+
return AdService:ShowRewardedVideoAdAsync(player, reward)
149+
end)
150+
151+
RewardedAdEvent:FireClient(player, isSuccess, result)
152+
end)
125153

126-
MarketplaceService.ProcessReceipt = processReceipt
127-
requestShowAdEvent.OnServerEvent:Connect(function(player)
128-
local reward = AdService:CreateAdRewardFromDevProductId(rewardDevProductId)
129-
local result = AdService:ShowRewardedVideoAdAsync(player, reward, placementId)
154+
MarketplaceService.ProcessReceipt = function(receiptInfo)
155+
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
156+
if not player then
157+
return Enum.ProductPurchaseDecision.NotProcessedYet
158+
end
130159

131-
-- Handle specific ad result cases
132-
if result == Enum.ShowAdResult.AdNotReady then
133-
warn("Ad not ready for player: " .. player.Name)
134-
elseif result == Enum.ShowAdResult.InternalError then
135-
warn("Internal error showing ad for player: " .. player.Name)
160+
if receiptInfo.ProductId == DEV_PRODUCT_ID then
161+
162+
-- Include the logic for granting rewards here
136163

137-
-- Add more cases as needed (for example, ShowInterrupted, AdAlreadyShowing, etc)
138-
...
164+
return Enum.ProductPurchaseDecision.PurchaseGranted
139165
end
140166

141-
-- Signal the Client to recheck if video ad is available
142-
recheckAdAvailabilityEvent:FireClient(player)
143-
end)
167+
return Enum.ProductPurchaseDecision.NotProcessedYet
168+
end
144169
```
145170

146171
## Placements
@@ -240,6 +265,8 @@ Any violation of the eligilibity requirements can result in account suspension,
240265

241266
To get the most out of your rewarded video ad, make sure to:
242267

268+
- Call `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` as close as possible to the moment you plan to show the ad. For example, if you have a "Watch video ad to get a reward" button in a shop menu, you should only call `GetAdAvailabilityNowAsync` when the user opens the shop menu. This approach improves performance by preventing ads from unnecessarily being held in memory, and benefits CPM (cost-per-thousand impressions) and earnings by optimizing the ad fill rate.
269+
- Call `Class.AdService.GetAdAvailabilityNowAsync|GetAdAvailabilityNowAsync` when the user finishes watching the ad to determine if another ad is available for the user to watch.
243270
- Scale rewards so that they remain valuable to your users as they advance through the experience.
244271
- Adjust the frequency of rewards based on user engagement and feedback.
245272
- Use analytics to identify the best placement for video ads to encourage user engagement without disrupting gameplay.

content/en-us/production/publishing/vr-guidelines.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ The following best practices may help you reach 72 frames per second with a high
5252
- Mobile VR is sensitive to a high number of draw calls. Build your environments efficiently, adding high detail where it really matters and lower detail elsewhere while being conservative with the number of objects used in the scene.
5353
- When creating custom 3D meshes, always strive to use as little geometry as possible for maximum runtime efficiency.
5454
- Minimize the number of semi-transparent objects and textures with partial transparency such as `Class.Decal|Decals` or the `Enum.Material|Glass` material.
55-
- Use **Voxel** or **ShadowMap** lighting `Class.Lighting.Technology|Technology`, as **Future** lighting can be costly to generate and may produce inconsistent results on VR when the automatic quality drops.
5655
- Numerous and complex `Class.SurfaceGui|SurfaceGuis` can be costly, both on the rendering and CPU side.
5756
- Avoid writing platform-dependent code such as actions that rely on keyboard presses. Instead, use objects like `Class.InputAction` which supports input bindings from multiple sources.
5857
- Test and iterate often to make sure you're getting the anticipated performance and visual quality. If possible, invest in a Quest&nbsp;2 headset.

content/en-us/studio/avatar-settings.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ The **Body** tab contains settings for body proportions, parts, scale.
110110

111111
## Clothing
112112

113+
<Alert severity = 'warning'>
114+
At this time, there is a bug impacting clothing controls. For current status, see the [DevForum announcement](https://devforum.roblox.com/t/introducing-avatar-settings-more-control-for-developers-better-expression-for-players/).
115+
</Alert>
116+
113117
The **Clothing** tab contains controls for layered and classic clothing.
114118

115119
<table>

content/en-us/studio/microprofiler/tag-table.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ When threads aren't actively performing tasks, they enter a sleep state, with ta
225225
</tr>
226226
<tr>
227227
<td>Perform/Scene/computeLightingPerform/LightGridCPU</td>
228-
<td>Updates the voxel lighting, which is used in `Enum.Technology|Voxel` and `Enum.Technology|ShadowMap` modes and at quality levels below 4 in `Enum.Technology|Future` mode.</td>
228+
<td>Updates the voxel lighting, which is used at lower quality levels.</td>
229229
<td>If updating chunk occupancy takes too long, consider using lower resolution geometry, reducing the number of parts, or anchoring parts. If the other sub-markers take too long, consider reducing the number of lights and using non-shadow casting geometry for objects that move and invalidate the occupancy.</td>
230230
</tr>
231231
<tr>
232232
<td>Perform/Scene/computeLightingPerform/ShadowMapSystem</td>
233-
<td>Updates shadow maps. Not performed at quality levels below 4 or when `Class.Lighting.Technology` is set to `Enum.Technology|Voxel`.</td>
234-
<td>If lighting is set to `Enum.Technology|Future`, lower it to `Enum.Technology|ShadowMap` or reduce the number of lights. You can also use `Class.Light.Shadows` and `Class.BasePart.CastShadows` to disable shadow casting on less important instances. See [Improving Performance](../../performance-optimization/improve.md#mitigation-4).</td>
233+
<td>Updates shadow maps. Not performed at quality levels below 4.</td>
234+
<td>Reduce the number of lights. You can also use `Class.Light.Shadows` and `Class.BasePart.CastShadows` to disable shadow casting on less important instances. See [Improving Performance](../../performance-optimization/improve.md#mitigation-4).</td>
235235
</tr>
236236
<tr>
237237
</tr>

0 commit comments

Comments
 (0)