com.azerion.bluestack 3.0.3 → 3.1.0-preview2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/Editor/Android/AndroidBuildPostProcess.cs +0 -1
  2. package/Editor/Android/AndroidDependency.cs +1 -2
  3. package/Editor/Android/AndroidDependencyModifier.cs +55 -0
  4. package/Editor/Android/AndroidDependencyModifier.cs.meta +3 -0
  5. package/Editor/Android/AndroidDependencyParser.cs +26 -1
  6. package/Editor/Android/ManifestProcessor.cs +2 -1
  7. package/Editor/Azerion.BlueStack.Editor.asmdef +14 -14
  8. package/Editor/BlueStackDependencies.xml +1 -1
  9. package/Editor/BlueStackMediationDependencies.xml +11 -0
  10. package/Editor/BlueStackMediationDependencies.xml.meta +7 -0
  11. package/Editor/BlueStackSettings.cs +21 -18
  12. package/Editor/BlueStackSettingsEditor.cs +64 -1
  13. package/Editor/DebugHelper.cs +2 -2
  14. package/Editor/Dependency.cs +1 -1
  15. package/Editor/DependencyProvider.cs +213 -2
  16. package/Editor/IDependencyModifier.cs +12 -0
  17. package/Editor/IDependencyModifier.cs.meta +11 -0
  18. package/Editor/IDependencyParser.cs +1 -0
  19. package/Editor/MediationNetworkDependency.cs +34 -0
  20. package/Editor/MediationNetworkDependency.cs.meta +3 -0
  21. package/Editor/iOS/IOSDependency.cs +12 -0
  22. package/Editor/iOS/IOSDependency.cs.meta +11 -0
  23. package/Editor/iOS/IOSDependencyModifier.cs +105 -0
  24. package/Editor/iOS/IOSDependencyModifier.cs.meta +3 -0
  25. package/Editor/iOS/IOSDependencyParser.cs +32 -0
  26. package/Editor/iOS/PodDependency/PodDependencyUtils.cs +5 -1
  27. package/Example/Azerion.BlueStack.Example.asmdef +14 -14
  28. package/Example/Scripts/NativeAdManager.cs +5 -7
  29. package/Example/Scripts/SmallNativeAdManager.cs +4 -6
  30. package/README.md +4 -1
  31. package/Runtime/API/AdSize.cs +2 -2
  32. package/Runtime/API/Banner/BannerAd.cs +1 -1
  33. package/Runtime/API/BlueStackAds.cs +7 -3
  34. package/Runtime/API/InterstitialAd.cs +20 -5
  35. package/Runtime/API/NativeAd/NativeAd.cs +20 -26
  36. package/Runtime/API/NativeAd/NativeAdEventArgs.cs +1 -5
  37. package/Runtime/API/NativeAd/NativeAdInteractionHandler.cs +176 -173
  38. package/Runtime/API/NativeAd/NativeAdLoader.cs +10 -14
  39. package/Runtime/API/NativeAd/NativeAdObject.cs +563 -547
  40. package/Runtime/API/NativeAd/NativeAdType.cs +6 -6
  41. package/Runtime/API/NativeAd/NativeAssetInfo.cs +28 -28
  42. package/Runtime/Azerion.BlueStack.asmdef +14 -14
  43. package/Runtime/Common/NativeAd/AdLoaderClientArgs.cs +8 -8
  44. package/Runtime/Common/NativeAd/AdsEventExecutor.cs +69 -68
  45. package/Runtime/Common/NativeAd/DummyNativeClient.cs +52 -52
  46. package/Runtime/Common/NativeAd/NativeClientEventArgs.cs +5 -5
  47. package/Runtime/Common/NativeAd/NativeUtils.cs +111 -107
  48. package/Runtime/Internal/IBannerAdClient.cs +1 -1
  49. package/Runtime/Internal/IBlueStackClient.cs +2 -1
  50. package/Runtime/Internal/NativeAd/IAdLoaderClient.cs +12 -12
  51. package/Runtime/Internal/NativeAd/INativeAdClient.cs +1 -1
  52. package/Runtime/Internal/NativeAd/INativeClientFactory.cs +5 -5
  53. package/Runtime/Platforms/Android/BlueStackClient.cs +2 -1
  54. package/Runtime/Platforms/Android/BlueStackNativeClassNames.cs +2 -1
  55. package/Runtime/Platforms/Unity/AdvertisementManager.cs +5 -4
  56. package/Runtime/Platforms/Unity/BannerAdClient.cs +16 -9
  57. package/Runtime/Platforms/Unity/BaseAdClient.cs +7 -5
  58. package/Runtime/Platforms/Unity/BlueStackClient.cs +4 -3
  59. package/Runtime/Platforms/Unity/ClientsFactory.cs +0 -1
  60. package/Runtime/Platforms/Unity/InitializationStatusClient.cs +1 -0
  61. package/Runtime/Platforms/Unity/InterstitialAdClient.cs +12 -6
  62. package/Runtime/Platforms/Unity/NativeAdClient.cs +16 -10
  63. package/Runtime/Platforms/Unity/RewardedVideoAdClient.cs +9 -3
  64. package/Runtime/Platforms/Unity/Utils.cs +7 -3
  65. package/Runtime/Platforms/iOS/BannerAdClient.cs +33 -24
  66. package/Runtime/Platforms/iOS/BlueStackClient.cs +14 -10
  67. package/Runtime/Platforms/iOS/ClientsFactory.cs +0 -2
  68. package/Runtime/Platforms/iOS/Externs.cs +72 -65
  69. package/Runtime/Platforms/iOS/InitializationStatusClient.cs +6 -5
  70. package/Runtime/Platforms/iOS/InterstitialAdClient.cs +27 -21
  71. package/Runtime/Platforms/iOS/NativeAdClient.cs +27 -22
  72. package/Runtime/Platforms/iOS/PreferenceClient.cs +1 -1
  73. package/Runtime/Platforms/iOS/RewardedVideoAdClient.cs +27 -20
  74. package/Runtime/Platforms/iOS/Utils.cs +6 -3
  75. package/Runtime/Utilities/MiniJSON.cs +12 -12
  76. package/package.json +1 -1
  77. package/Editor/iOS/Dependency.cs +0 -0
  78. package/Editor/iOS/Dependency.cs.meta +0 -3
@@ -9,550 +9,566 @@ using UnityEngine.UI;
9
9
 
10
10
  namespace Azerion.BlueStack.API
11
11
  {
12
- // Struct to store screen space bounds
13
- internal struct VisibilityInfo
14
- {
15
- public float PercentageOfVisibleArea; // Percentage of visible area
16
- public float PercentageOfScreenSpace; // Percentage of screen space
17
- }
18
-
19
- internal struct ClickInfo
20
- {
21
- public Vector3 ClickPosition;
22
- public float ClickTime;
23
- }
24
-
25
- internal class ClickEventArgs : EventArgs
26
- {
27
- public ClickInfo StartClick { get; set; }
28
- public ClickInfo EndClick { get; set; }
29
- }
30
-
31
- internal enum HitStatus
32
- {
33
- Miss,
34
- ObstructedHit,
35
- UnobstructedHit,
36
- }
37
-
38
- [AddComponentMenu(""), DisallowMultipleComponent, HideInInspector]
39
- internal sealed class NativeAdObject : MonoBehaviour
40
- {
41
- private const float _timeInterval = 0.1f;
42
- private const int _iterations = 5;
43
- private float _lastInteractionTime;
44
- private ClickInfo _clickInfo;
45
- private bool _is2DColliderPresent;
46
- private bool _isInteractionStarted;
47
- private bool _isRectTransPresent; // check again
48
- internal event EventHandler<EventArgs> OnAdDisplay;
49
- internal event EventHandler<ClickEventArgs> OnAdClick;
50
- internal bool IsImpressionChecked;
51
- internal bool HasMadeImpression;
52
- internal bool IsImpressive;
53
- internal bool IsClickable;
54
-
55
- internal float InteractionTime { get; private set; }
56
-
57
- internal string ObjectTag { get; private set; }
58
-
59
- public void Start()
60
- {
61
- this._lastInteractionTime = 0f;
62
- this.IsImpressionChecked = false;
63
- }
64
-
65
- public void Initialize(string nativeAssetID, bool isImpressive = false, bool isClickable = false)
66
- {
67
- ObjectTag = nativeAssetID;
68
- IsImpressive = isImpressive;
69
- IsClickable = isClickable;
70
-
71
- if (GetComponent<Collider>() == null)
72
- {
73
- if (GetComponent<Collider2D>() == null)
74
- {
75
- throw new InvalidOperationException(" No Collider or Collider2D component found.");
76
- }
77
- else
78
- {
79
- _is2DColliderPresent = true;
80
- Debug.Log("2D Collider Present!!!");
81
- }
82
- }
83
- else
84
- {
85
- Debug.Log("Collider Present!!!");
86
- }
87
-
88
- // Check if the GameObject has a Renderer component.
89
- Renderer rendererComponent = GetComponent<Renderer>();
90
- if (rendererComponent != null)
91
- {
92
- if (!rendererComponent.isVisible)
93
- {
94
- throw new InvalidOperationException(" Object is not visible.");
95
- }
96
- }
97
- else if (IsValidUIObject())
98
- {
99
- Debug.Log("IsValidUIObject");
100
- _isRectTransPresent = true;
101
- }
102
- else
103
- {
104
- throw new InvalidOperationException(" Not a valid Ad Object.");
105
- }
106
-
107
- if(IsImpressive)
108
- InvokeRepeating("CheckForImpression", 0.0f, this.TimeInterval());
109
- }
110
-
111
- ClickInfo _startClick;
112
- ClickInfo _endClick;
113
- public void Update()
114
- {
115
- if(!IsClickable) return;
116
-
117
- // track clickable elements
118
- this._lastInteractionTime += (Time.deltaTime - this._lastInteractionTime) * 0.1f;
119
-
120
- if (Input.GetMouseButtonDown(0))
121
- {
122
- this._isInteractionStarted = true;
123
- _startClick.ClickPosition = Input.mousePosition;
124
- _startClick.ClickTime = Time.time * 1000f;
125
- }
126
- else if (this._isInteractionStarted && Input.GetMouseButtonUp(0))
127
- {
128
- this._isInteractionStarted = false;
129
- _endClick.ClickPosition = Input.mousePosition;
130
- _endClick.ClickTime = Time.time * 1000f;
131
-
132
- HitStatus hitStatus;
133
-
134
- if (!_is2DColliderPresent)
135
- {
136
- hitStatus = CheckHitStatus3D(Input.mousePosition);
137
-
138
- }
139
- else
140
- {
141
- hitStatus = CheckHitStatus2D(Input.mousePosition);
142
- }
143
-
144
- if (hitStatus == HitStatus.UnobstructedHit)
145
- {
146
- OnAdClick?.Invoke(this, new ClickEventArgs { StartClick = _startClick, EndClick = _endClick });
147
- }
148
- }
149
- }
150
-
151
- // Method to get screen space bounds
152
- internal VisibilityInfo GetVisibilityInfo()
153
- {
154
- // Get camera component
155
- Camera mainCamera = NativeUtils.GetCamera();
156
- VisibilityInfo visibilityInfo = new VisibilityInfo(); // Create a new struct
157
- // Get gameobject to check bounds for
158
- GameObject currentGameObject = this.gameObject;
159
- // Get world space bounds
160
- Bounds worldSpaceBounds;
161
- if (!_is2DColliderPresent) // Check if 3D object
162
- {
163
- worldSpaceBounds = currentGameObject.GetComponent<Collider>().bounds;
164
- }
165
- else
166
- {
167
- worldSpaceBounds = currentGameObject.GetComponent<Collider2D>().bounds;
168
- }
169
- // Get max bounds
170
- Vector3 maxBounds = NativeUtils.GetBoundsMax(currentGameObject, mainCamera, worldSpaceBounds);
171
- // Get min bounds
172
- Vector3 minBounds = NativeUtils.GetBoundsMin(currentGameObject, mainCamera, worldSpaceBounds);
173
-
174
- // Calculate screen space bounds
175
- float minX = Math.Max(0.0f, minBounds.x);
176
- float minY = Math.Max(0.0f, minBounds.y);
177
- float maxX = Math.Min(Screen.width, maxBounds.x);
178
- float maxY = Math.Min(Screen.height, maxBounds.y);
179
-
180
- // Increment amounts
181
- int xDivisions = (int) ((maxX - minX) / 5.0);
182
- int yDivisions = (int) ((maxY - minY) / 5.0);
183
-
184
- // Check if center is visible
185
- if (xDivisions <= 0 || yDivisions <= 0)
186
- {
187
- return visibilityInfo;
188
- }
189
-
190
- // Calculate actual area
191
- float actualWidth = maxBounds.x - minBounds.x;
192
- float actualHeight = maxBounds.y - minBounds.y;
193
-
194
- // Calculate screen space area
195
- float width = maxX - minX;
196
- float height = maxY - minY;
197
-
198
- // Check if height, width are neg
199
- if (width <= 0.0f || height <= 0.0f)
200
- {
201
- return visibilityInfo;
202
- }
203
- float areaOnScreen = width * height;
204
- float percentageOfAreaOnScreen = areaOnScreen / (actualWidth * actualHeight);
205
-
206
- // visibilityInfo.PercentageOfVisibleArea = !this._is2DColliderPresent ? this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions)
207
- // : this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions);
208
-
209
- // Set values in Visibility Info
210
- visibilityInfo.PercentageOfVisibleArea = CalculatePercentageOfVisibleArea(minX, maxX, minY, maxY, xDivisions, yDivisions, percentageOfAreaOnScreen);
211
- visibilityInfo.PercentageOfScreenSpace = areaOnScreen / (Screen.width * Screen.height);
212
- return visibilityInfo;
213
- }
214
-
215
- // Methods to check if the given input vector is obstructed or unobstructed
216
- private HitStatus CheckHitStatus2D(Vector3 position)
217
- {
218
- // Create a ray from the camera to the specified screen point
219
- Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
220
-
221
- // Perform a 2D raycast and store the result
222
- var rayIntersection = Physics2D.GetRayIntersection(ray);
223
- // Check if the raycast hit is obstructed by a Collider component
224
- if ((UnityEngine.Object)rayIntersection.collider != null)
225
- {
226
- if (rayIntersection.collider == GetComponent<Collider2D>())
227
- {
228
- // Perform a 3D raycast and store the result
229
- if (Physics.Raycast(ray, out var hitInfo, float.PositiveInfinity))
230
- {
231
- // Check if the 3D raycast hit is closer than the 2D raycast hit
232
- if (rayIntersection.distance >= hitInfo.distance)
233
- {
234
- return HitStatus.ObstructedHit;
235
- }
236
- }
237
- // Return unobstructed hit if no obstructions were found
238
- return HitStatus.UnobstructedHit;
239
- }
240
- }
241
-
242
- // Perform a 2D raycast and store the results in an array
243
- RaycastHit2D[] rayIntersectionAll = Physics2D.GetRayIntersectionAll(ray);
244
- // Loop through the raycast hits
245
- foreach (var raycastHit2D in rayIntersectionAll)
246
- {
247
- // Check if the raycast hit is obstructed by the Collider2D component
248
- if (raycastHit2D.collider == GetComponent<Collider2D>())
249
- {
250
- return HitStatus.ObstructedHit;
251
- }
252
- }
253
- return this.CheckHitStatusCanvas(position);
254
- }
255
-
256
- private HitStatus CheckHitStatusCanvas(Vector3 position)
257
- {
258
- GraphicRaycaster parentGraphicRaycaster = GetComponentInParent<GraphicRaycaster>();
259
- if(parentGraphicRaycaster == null)
260
- {
261
- return HitStatus.Miss;
262
- }
263
- EventSystem currentEventSystem = EventSystem.current;
264
- if(currentEventSystem == null)
265
- {
266
- return HitStatus.Miss;
267
- }
268
- PointerEventData pointerEventData = new PointerEventData(currentEventSystem);
269
- pointerEventData.position = position;
270
- List<RaycastResult> raycastResults = new List<RaycastResult>();
271
- parentGraphicRaycaster.Raycast(pointerEventData, raycastResults);
272
- GameObject hitGameObject = null;
273
- bool isHit = false;
274
- int maxDepth = 0;
275
- foreach(RaycastResult result in raycastResults)
276
- {
277
- if(result.gameObject == gameObject)
278
- {
279
- isHit = true;
280
- }
281
- if(result.depth > maxDepth)
282
- {
283
- maxDepth = result.depth;
284
- hitGameObject = result.gameObject;
285
- }
286
- }
287
- if(!isHit)
288
- {
289
- return HitStatus.Miss;
290
- }
291
- if(hitGameObject == gameObject)
292
- {
293
- return HitStatus.UnobstructedHit;
294
- }
295
- else
296
- {
297
- return HitStatus.ObstructedHit;
298
- }
299
- }
300
-
301
- private HitStatus CheckHitStatus3D(Vector3 position)
302
- {
303
- // Create a ray from the camera to the input vector
304
- Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
305
- // Initialize variables for raycast hits
306
- RaycastHit hitInfo;
307
- RaycastHit2D rayIntersection;
308
- // bool isHit = GetComponent<Collider>().Raycast(ray, out hitInfo, float.PositiveInfinity);
309
-
310
- // Check for 3D raycast hit
311
- if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity))
312
- {
313
- // Check if the hit object is the current object
314
- if (hitInfo.collider == GetComponent<Collider>())
315
- {
316
- return HitStatus.UnobstructedHit;
317
- }
318
- else
319
- {
320
- // Check for 2D raycast hit
321
- rayIntersection = Physics2D.GetRayIntersection(ray);
322
- if ((UnityEngine.Object)rayIntersection.collider != null &&
323
- rayIntersection.distance <= hitInfo.distance)
324
- {
325
- return HitStatus.UnobstructedHit;
326
- }
327
- else
328
- {
329
- return HitStatus.ObstructedHit;
330
- }
331
- }
332
- }
333
- return this.CheckHitStatusCanvas(position);
334
- }
335
-
336
- // Method to check for Unobstructed Visible Area in screen space
337
- private float CalculatePercentageOfVisibleArea(float minX, float maxX, float minY, float maxY, int xDivisions, int yDivisions, float percentageOfAreaOnScreen)
338
- {
339
- // Initialize variables
340
- float numUnobstructedPoints = 0.0f;
341
- float totalPoints = 0.0f;
342
- float currentX = minX;
343
- float currentY = minY;
344
- // Increment through screen space
345
- while (currentX <= maxX)
346
- {
347
- while (currentY <= maxY)
348
- {
349
- // Check if point is unobstructed
350
- HitStatus obstructionCheckResult = !_is2DColliderPresent
351
- ? CheckHitStatus3D(new Vector3(currentX, currentY, 0.0f))
352
- : CheckHitStatus2D(new Vector3(currentX, currentY, 0.0f));
353
- if (obstructionCheckResult == HitStatus.UnobstructedHit)
354
- {
355
- numUnobstructedPoints++;
356
- totalPoints++;
357
- }
358
- else if (obstructionCheckResult == HitStatus.ObstructedHit)
359
- {
360
- totalPoints++;
361
- }
362
- currentY += yDivisions;
363
- }
364
- currentX += xDivisions;
365
- currentY = minY;
366
- }
367
-
368
- return percentageOfAreaOnScreen * (numUnobstructedPoints / totalPoints);
369
- }
370
-
371
- public void OnDestroy()
372
- {
373
- base.CancelInvoke();
374
- }
375
-
376
- public void OnBecameVisible()
377
- {
378
- if (!this.IsImpressionChecked && IsImpressive)
379
- {
380
- InvokeRepeating("CheckForImpression", 1.0f, this.TimeInterval());
381
- }
382
- }
383
-
384
- public void OnBecameInvisible() => CancelInvoke();
385
-
386
- public void StopImpressionCheck()
387
- {
388
- base.CancelInvoke();
389
- this.IsImpressionChecked = true;
390
- }
391
-
392
- // Method to check if a UI element overlaps with the screen
393
- private bool DoesUIElementOverlapScreen()
394
- {
395
- // Get the RectTransform component
396
- RectTransform rectTransform = this.GetComponent<RectTransform>();
397
- // Create a rect for the screen size
398
- Rect screenRect = new Rect(0.0f, 0.0f, Screen.width, Screen.height);
399
- // Get the anchored position of the UI element
400
- float x = rectTransform.anchoredPosition.x + rectTransform.position.x;
401
- float y = Screen.height - rectTransform.position.y - rectTransform.anchoredPosition.y;
402
- // Get the size of the UI element
403
- Vector2 elementSize = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
404
- // Create a rect for the UI element
405
- Rect elementRect = new Rect(x, y, elementSize.x, elementSize.y);
406
- // Check if the UI element rect overlaps with the screen rect
407
- Debug.LogWarning("Does screenRect.Overlaps: " + screenRect.Overlaps(elementRect, false));
408
- if (screenRect.Overlaps(elementRect, false))
409
- {
410
- return true;
411
- }
412
- return false;
413
- }
414
-
415
- // Method to check if a gameobject is in the camera's view frustum
416
- private bool IsInViewFrustum()
417
- {
418
- // Check if the gameobject is rendered in the screen space overlay canvas
419
- if (!NativeUtils.IsRenderedInScreenSpaceOverlayCanvas(gameObject))
420
- {
421
- // Get the camera component
422
- Camera mainCamera = NativeUtils.GetCamera();
423
- // Get the bounds of the gameobject
424
- Bounds bounds;
425
- if (_is2DColliderPresent)
426
- {
427
- bounds = this.GetComponent<Collider2D>().bounds;
428
- }
429
- else
430
- {
431
- bounds = this.GetComponent<Collider>().bounds;
432
- }
433
- // Debug.LogWarning("bounds: " + bounds);
434
-
435
- // Get the view frustum planes
436
- Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
437
-
438
- // Check if the gameobject bounds intersect with the view frustum planes
439
- // Debug.LogWarning("TestPlanesAABB: " + GeometryUtility.TestPlanesAABB(frustumPlanes, bounds));
440
- if (!GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
441
- {
442
- return false;
443
- }
444
-
445
- // Get the max bounds of the gameobject
446
- Vector3 maxBounds = NativeUtils.GetBoundsMax(this.gameObject, mainCamera, bounds);
447
- // Get the min bounds of the gameobject
448
- Vector3 minBounds = NativeUtils.GetBoundsMin(this.gameObject, mainCamera, bounds);
449
-
450
- float height = Math.Min(Screen.height, maxBounds.y) - Math.Max(0.0f, minBounds.y);
451
- float width = Math.Min(Screen.width, maxBounds.x) - Math.Max(0.0f, minBounds.x);
452
-
453
- // Debug.LogWarning("height: " + height);
454
- // Debug.LogWarning("width: " + width);
455
-
456
- // Check if the gameobject bounds are too small to be visible
457
- if (height <= 0.01f * Screen.height || width <= 0.01f * Screen.width)
458
- {
459
- return false;
460
- }
461
- }
462
- else // When rendered in the screen space overlay canvas
463
- {
464
- Debug.LogWarning("DoesUIElementOverlapScreen: " + DoesUIElementOverlapScreen());
465
-
466
- // Check if the UI element overlaps with the screen
467
- if (!DoesUIElementOverlapScreen())
468
- {
469
- return false;
470
- }
471
- }
472
-
473
- return true;
474
- }
475
-
476
- private void CheckForImpression()
477
- {
478
- VisibilityInfo visibilityInfo = GetVisibilityInfo();
479
- if (!IsImpressionChecked && IsInViewFrustum())
480
- {
481
- if (visibilityInfo.PercentageOfVisibleArea >= 0.5f) // if Visible Area is >= 50%
482
- {
483
- HasMadeImpression = true;
484
- OnAdDisplay?.Invoke(this, EventArgs.Empty);
485
- }
486
- else
487
- {
488
- HasMadeImpression = false;
489
- }
490
- }
491
- }
492
- private float TimeInterval()
493
- {
494
- return 1.0f;
495
- }
496
-
497
- // TODO: More testing and Improvement required
498
- // TODO: BSSDK-401 : Move viewability to container level - may not have a container
499
- private bool IsValidUIObject()
500
- {
501
- Canvas canvas = GetComponentInParent<Canvas>();
502
- if (canvas == null)
503
- {
504
- Debug.Log("Unable to find Canvas component in parents");
505
- return false;
506
- }
507
-
508
- RectTransform rectTransform = GetComponent<RectTransform>();
509
- if (rectTransform == null)
510
- {
511
- Debug.Log("RectTransform component is null");
512
- return false;
513
- }
514
-
515
- CanvasRenderer canvasRenderer = GetComponent<CanvasRenderer>();
516
- if (canvasRenderer == null)
517
- {
518
- Debug.Log("CanvasRenderer component is null");
519
- return false;
520
- }
521
-
522
- // Rect rect = rectTransform.rect;
523
- //
524
- // // Get the center position of the RectTransform in its local coordinates
525
- // Vector2 centerLocalPosition = new Vector2(rect.center.x, rect.center.y);
526
- //
527
- // // Convert the local center position to world position
528
- // Vector3 centerWorldPosition = rectTransform.TransformPoint(centerLocalPosition);
529
- // Debug.Log("centerWorldPosition: " + centerWorldPosition);
530
- //
531
- // Vector3 viewportPoint = NativeUtils.GetCamera().WorldToViewportPoint(centerWorldPosition);
532
- // Debug.Log("viewportPoints: " + viewportPoint);
533
- // if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
534
- // {
535
- // return true;
536
- // }
537
-
538
- // Check if it is completely inside the viewport
539
- // Vector3[] corners = new Vector3[4];
540
- // rectTransform.GetWorldCorners(corners);
541
- // Debug.Log("Ad Object: " + this.name + " - " + corners.Length);
542
- // foreach (Vector3 corner in corners)
543
- // {
544
- // // Debug.Log("corner: " + corner);
545
- // Vector3 viewportPoint = Camera.main.WorldToViewportPoint(corner);
546
- // // Debug.Log("viewportPoints: " + viewportPoint);
547
- // if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
548
- // {
549
- // //return true;
550
- // }
551
- // }
552
-
553
- // Debug.Log("Not a Valid UI Object!");
554
-
555
- return true;
556
- }
557
- }
558
- }
12
+ // Struct to store screen space bounds
13
+ internal struct VisibilityInfo
14
+ {
15
+ public float PercentageOfVisibleArea; // Percentage of visible area
16
+ public float PercentageOfScreenSpace; // Percentage of screen space
17
+ }
18
+
19
+ internal struct ClickInfo
20
+ {
21
+ public Vector3 ClickPosition;
22
+ public float ClickTime;
23
+ }
24
+
25
+ internal class ClickEventArgs : EventArgs
26
+ {
27
+ public ClickInfo StartClick { get; set; }
28
+ public ClickInfo EndClick { get; set; }
29
+ }
30
+
31
+ internal enum HitStatus
32
+ {
33
+ Miss,
34
+ ObstructedHit,
35
+ UnobstructedHit,
36
+ }
37
+
38
+ [AddComponentMenu(""), DisallowMultipleComponent, HideInInspector]
39
+ internal sealed class NativeAdObject : MonoBehaviour
40
+ {
41
+ private const float _timeInterval = 0.1f;
42
+ private const int _iterations = 5;
43
+ private float _lastInteractionTime;
44
+ private ClickInfo _clickInfo;
45
+ private bool _is2DColliderPresent;
46
+ private bool _isInteractionStarted;
47
+ private bool _isRectTransPresent; // check again
48
+ internal event EventHandler<EventArgs> OnAdDisplay;
49
+ internal event EventHandler<ClickEventArgs> OnAdClick;
50
+ internal bool IsImpressionChecked;
51
+ internal bool HasMadeImpression;
52
+ internal bool IsImpressive;
53
+ internal bool IsClickable;
54
+
55
+ internal float InteractionTime { get; private set; }
56
+
57
+ internal string ObjectTag { get; private set; }
58
+
59
+ public void Start()
60
+ {
61
+ this._lastInteractionTime = 0f;
62
+ this.IsImpressionChecked = false;
63
+ }
64
+
65
+ public void Initialize(string nativeAssetID, bool isImpressive = false, bool isClickable = false)
66
+ {
67
+ ObjectTag = nativeAssetID;
68
+ IsImpressive = isImpressive;
69
+ IsClickable = isClickable;
70
+
71
+ if (GetComponent<Collider>() == null)
72
+ {
73
+ if (GetComponent<Collider2D>() == null)
74
+ {
75
+ throw new InvalidOperationException(" No Collider or Collider2D component found.");
76
+ }
77
+ else
78
+ {
79
+ _is2DColliderPresent = true;
80
+ Debug.Log("2D Collider Present!!!");
81
+ }
82
+ }
83
+ else
84
+ {
85
+ Debug.Log("Collider Present!!!");
86
+ }
87
+
88
+ // Check if the GameObject has a Renderer component.
89
+ Renderer rendererComponent = GetComponent<Renderer>();
90
+ if (rendererComponent != null)
91
+ {
92
+ if (!rendererComponent.isVisible)
93
+ {
94
+ throw new InvalidOperationException(" Object is not visible.");
95
+ }
96
+ }
97
+ else if (IsValidUIObject())
98
+ {
99
+ Debug.Log("IsValidUIObject");
100
+ _isRectTransPresent = true;
101
+ }
102
+ else
103
+ {
104
+ throw new InvalidOperationException(" Not a valid Ad Object.");
105
+ }
106
+
107
+ if (IsImpressive)
108
+ InvokeRepeating("CheckForImpression", 0.0f, this.TimeInterval());
109
+ }
110
+
111
+ ClickInfo _startClick;
112
+ ClickInfo _endClick;
113
+
114
+ public void Update()
115
+ {
116
+ if (!IsClickable) return;
117
+
118
+ // track clickable elements
119
+ this._lastInteractionTime += (Time.deltaTime - this._lastInteractionTime) * 0.1f;
120
+
121
+ if (Input.GetMouseButtonDown(0))
122
+ {
123
+ this._isInteractionStarted = true;
124
+ _startClick.ClickPosition = Input.mousePosition;
125
+ _startClick.ClickTime = Time.time * 1000f;
126
+ }
127
+ else if (this._isInteractionStarted && Input.GetMouseButtonUp(0))
128
+ {
129
+ this._isInteractionStarted = false;
130
+ _endClick.ClickPosition = Input.mousePosition;
131
+ _endClick.ClickTime = Time.time * 1000f;
132
+
133
+ HitStatus hitStatus;
134
+
135
+ if (!_is2DColliderPresent)
136
+ {
137
+ hitStatus = CheckHitStatus3D(Input.mousePosition);
138
+ }
139
+ else
140
+ {
141
+ hitStatus = CheckHitStatus2D(Input.mousePosition);
142
+ }
143
+
144
+ if (hitStatus == HitStatus.UnobstructedHit)
145
+ {
146
+ OnAdClick?.Invoke(this, new ClickEventArgs { StartClick = _startClick, EndClick = _endClick });
147
+ }
148
+ }
149
+ }
150
+
151
+ // Method to get screen space bounds
152
+ internal VisibilityInfo GetVisibilityInfo()
153
+ {
154
+ // Get camera component
155
+ Camera mainCamera = NativeUtils.GetCamera();
156
+ VisibilityInfo visibilityInfo = new VisibilityInfo(); // Create a new struct
157
+ // Get gameobject to check bounds for
158
+ GameObject currentGameObject = this.gameObject;
159
+ // Get world space bounds
160
+ Bounds worldSpaceBounds;
161
+ if (!_is2DColliderPresent) // Check if 3D object
162
+ {
163
+ worldSpaceBounds = currentGameObject.GetComponent<Collider>().bounds;
164
+ }
165
+ else
166
+ {
167
+ worldSpaceBounds = currentGameObject.GetComponent<Collider2D>().bounds;
168
+ }
169
+
170
+ // Get max bounds
171
+ Vector3 maxBounds = NativeUtils.GetBoundsMax(currentGameObject, mainCamera, worldSpaceBounds);
172
+ // Get min bounds
173
+ Vector3 minBounds = NativeUtils.GetBoundsMin(currentGameObject, mainCamera, worldSpaceBounds);
174
+
175
+ // Calculate screen space bounds
176
+ float minX = Math.Max(0.0f, minBounds.x);
177
+ float minY = Math.Max(0.0f, minBounds.y);
178
+ float maxX = Math.Min(Screen.width, maxBounds.x);
179
+ float maxY = Math.Min(Screen.height, maxBounds.y);
180
+
181
+ // Increment amounts
182
+ int xDivisions = (int)((maxX - minX) / 5.0);
183
+ int yDivisions = (int)((maxY - minY) / 5.0);
184
+
185
+ // Check if center is visible
186
+ if (xDivisions <= 0 || yDivisions <= 0)
187
+ {
188
+ return visibilityInfo;
189
+ }
190
+
191
+ // Calculate actual area
192
+ float actualWidth = maxBounds.x - minBounds.x;
193
+ float actualHeight = maxBounds.y - minBounds.y;
194
+
195
+ // Calculate screen space area
196
+ float width = maxX - minX;
197
+ float height = maxY - minY;
198
+
199
+ // Check if height, width are neg
200
+ if (width <= 0.0f || height <= 0.0f)
201
+ {
202
+ return visibilityInfo;
203
+ }
204
+
205
+ float areaOnScreen = width * height;
206
+ float percentageOfAreaOnScreen = areaOnScreen / (actualWidth * actualHeight);
207
+
208
+ // visibilityInfo.PercentageOfVisibleArea = !this._is2DColliderPresent ? this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions)
209
+ // : this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions);
210
+
211
+ // Set values in Visibility Info
212
+ visibilityInfo.PercentageOfVisibleArea = CalculatePercentageOfVisibleArea(minX, maxX, minY, maxY,
213
+ xDivisions, yDivisions, percentageOfAreaOnScreen);
214
+ visibilityInfo.PercentageOfScreenSpace = areaOnScreen / (Screen.width * Screen.height);
215
+ return visibilityInfo;
216
+ }
217
+
218
+ // Methods to check if the given input vector is obstructed or unobstructed
219
+ private HitStatus CheckHitStatus2D(Vector3 position)
220
+ {
221
+ // Create a ray from the camera to the specified screen point
222
+ Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
223
+
224
+ // Perform a 2D raycast and store the result
225
+ var rayIntersection = Physics2D.GetRayIntersection(ray);
226
+ // Check if the raycast hit is obstructed by a Collider component
227
+ if ((UnityEngine.Object)rayIntersection.collider != null)
228
+ {
229
+ if (rayIntersection.collider == GetComponent<Collider2D>())
230
+ {
231
+ // Perform a 3D raycast and store the result
232
+ if (Physics.Raycast(ray, out var hitInfo, float.PositiveInfinity))
233
+ {
234
+ // Check if the 3D raycast hit is closer than the 2D raycast hit
235
+ if (rayIntersection.distance >= hitInfo.distance)
236
+ {
237
+ return HitStatus.ObstructedHit;
238
+ }
239
+ }
240
+
241
+ // Return unobstructed hit if no obstructions were found
242
+ return HitStatus.UnobstructedHit;
243
+ }
244
+ }
245
+
246
+ // Perform a 2D raycast and store the results in an array
247
+ RaycastHit2D[] rayIntersectionAll = Physics2D.GetRayIntersectionAll(ray);
248
+ // Loop through the raycast hits
249
+ foreach (var raycastHit2D in rayIntersectionAll)
250
+ {
251
+ // Check if the raycast hit is obstructed by the Collider2D component
252
+ if (raycastHit2D.collider == GetComponent<Collider2D>())
253
+ {
254
+ return HitStatus.ObstructedHit;
255
+ }
256
+ }
257
+
258
+ return this.CheckHitStatusCanvas(position);
259
+ }
260
+
261
+ private HitStatus CheckHitStatusCanvas(Vector3 position)
262
+ {
263
+ GraphicRaycaster parentGraphicRaycaster = GetComponentInParent<GraphicRaycaster>();
264
+ if (parentGraphicRaycaster == null)
265
+ {
266
+ return HitStatus.Miss;
267
+ }
268
+
269
+ EventSystem currentEventSystem = EventSystem.current;
270
+ if (currentEventSystem == null)
271
+ {
272
+ return HitStatus.Miss;
273
+ }
274
+
275
+ PointerEventData pointerEventData = new PointerEventData(currentEventSystem);
276
+ pointerEventData.position = position;
277
+ List<RaycastResult> raycastResults = new List<RaycastResult>();
278
+ parentGraphicRaycaster.Raycast(pointerEventData, raycastResults);
279
+ GameObject hitGameObject = null;
280
+ bool isHit = false;
281
+ int maxDepth = 0;
282
+ foreach (RaycastResult result in raycastResults)
283
+ {
284
+ if (result.gameObject == gameObject)
285
+ {
286
+ isHit = true;
287
+ }
288
+
289
+ if (result.depth > maxDepth)
290
+ {
291
+ maxDepth = result.depth;
292
+ hitGameObject = result.gameObject;
293
+ }
294
+ }
295
+
296
+ if (!isHit)
297
+ {
298
+ return HitStatus.Miss;
299
+ }
300
+
301
+ if (hitGameObject == gameObject)
302
+ {
303
+ return HitStatus.UnobstructedHit;
304
+ }
305
+ else
306
+ {
307
+ return HitStatus.ObstructedHit;
308
+ }
309
+ }
310
+
311
+ private HitStatus CheckHitStatus3D(Vector3 position)
312
+ {
313
+ // Create a ray from the camera to the input vector
314
+ Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
315
+ // Initialize variables for raycast hits
316
+ RaycastHit hitInfo;
317
+ RaycastHit2D rayIntersection;
318
+ // bool isHit = GetComponent<Collider>().Raycast(ray, out hitInfo, float.PositiveInfinity);
319
+
320
+ // Check for 3D raycast hit
321
+ if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity))
322
+ {
323
+ // Check if the hit object is the current object
324
+ if (hitInfo.collider == GetComponent<Collider>())
325
+ {
326
+ return HitStatus.UnobstructedHit;
327
+ }
328
+ else
329
+ {
330
+ // Check for 2D raycast hit
331
+ rayIntersection = Physics2D.GetRayIntersection(ray);
332
+ if ((UnityEngine.Object)rayIntersection.collider != null &&
333
+ rayIntersection.distance <= hitInfo.distance)
334
+ {
335
+ return HitStatus.UnobstructedHit;
336
+ }
337
+ else
338
+ {
339
+ return HitStatus.ObstructedHit;
340
+ }
341
+ }
342
+ }
343
+
344
+ return this.CheckHitStatusCanvas(position);
345
+ }
346
+
347
+ // Method to check for Unobstructed Visible Area in screen space
348
+ private float CalculatePercentageOfVisibleArea(float minX, float maxX, float minY, float maxY, int xDivisions,
349
+ int yDivisions, float percentageOfAreaOnScreen)
350
+ {
351
+ // Initialize variables
352
+ float numUnobstructedPoints = 0.0f;
353
+ float totalPoints = 0.0f;
354
+ float currentX = minX;
355
+ float currentY = minY;
356
+ // Increment through screen space
357
+ while (currentX <= maxX)
358
+ {
359
+ while (currentY <= maxY)
360
+ {
361
+ // Check if point is unobstructed
362
+ HitStatus obstructionCheckResult = !_is2DColliderPresent
363
+ ? CheckHitStatus3D(new Vector3(currentX, currentY, 0.0f))
364
+ : CheckHitStatus2D(new Vector3(currentX, currentY, 0.0f));
365
+ if (obstructionCheckResult == HitStatus.UnobstructedHit)
366
+ {
367
+ numUnobstructedPoints++;
368
+ totalPoints++;
369
+ }
370
+ else if (obstructionCheckResult == HitStatus.ObstructedHit)
371
+ {
372
+ totalPoints++;
373
+ }
374
+
375
+ currentY += yDivisions;
376
+ }
377
+
378
+ currentX += xDivisions;
379
+ currentY = minY;
380
+ }
381
+
382
+ return percentageOfAreaOnScreen * (numUnobstructedPoints / totalPoints);
383
+ }
384
+
385
+ public void OnDestroy()
386
+ {
387
+ base.CancelInvoke();
388
+ }
389
+
390
+ public void OnBecameVisible()
391
+ {
392
+ if (!this.IsImpressionChecked && IsImpressive)
393
+ {
394
+ InvokeRepeating("CheckForImpression", 1.0f, this.TimeInterval());
395
+ }
396
+ }
397
+
398
+ public void OnBecameInvisible() => CancelInvoke();
399
+
400
+ public void StopImpressionCheck()
401
+ {
402
+ base.CancelInvoke();
403
+ this.IsImpressionChecked = true;
404
+ }
405
+
406
+ // Method to check if a UI element overlaps with the screen
407
+ private bool DoesUIElementOverlapScreen()
408
+ {
409
+ // Get the RectTransform component
410
+ RectTransform rectTransform = this.GetComponent<RectTransform>();
411
+ // Create a rect for the screen size
412
+ Rect screenRect = new Rect(0.0f, 0.0f, Screen.width, Screen.height);
413
+ // Get the anchored position of the UI element
414
+ float x = rectTransform.anchoredPosition.x + rectTransform.position.x;
415
+ float y = Screen.height - rectTransform.position.y - rectTransform.anchoredPosition.y;
416
+ // Get the size of the UI element
417
+ Vector2 elementSize = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
418
+ // Create a rect for the UI element
419
+ Rect elementRect = new Rect(x, y, elementSize.x, elementSize.y);
420
+ // Check if the UI element rect overlaps with the screen rect
421
+ Debug.LogWarning("Does screenRect.Overlaps: " + screenRect.Overlaps(elementRect, false));
422
+ if (screenRect.Overlaps(elementRect, false))
423
+ {
424
+ return true;
425
+ }
426
+
427
+ return false;
428
+ }
429
+
430
+ // Method to check if a gameobject is in the camera's view frustum
431
+ private bool IsInViewFrustum()
432
+ {
433
+ // Check if the gameobject is rendered in the screen space overlay canvas
434
+ if (!NativeUtils.IsRenderedInScreenSpaceOverlayCanvas(gameObject))
435
+ {
436
+ // Get the camera component
437
+ Camera mainCamera = NativeUtils.GetCamera();
438
+ // Get the bounds of the gameobject
439
+ Bounds bounds;
440
+ if (_is2DColliderPresent)
441
+ {
442
+ bounds = this.GetComponent<Collider2D>().bounds;
443
+ }
444
+ else
445
+ {
446
+ bounds = this.GetComponent<Collider>().bounds;
447
+ }
448
+ // Debug.LogWarning("bounds: " + bounds);
449
+
450
+ // Get the view frustum planes
451
+ Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
452
+
453
+ // Check if the gameobject bounds intersect with the view frustum planes
454
+ // Debug.LogWarning("TestPlanesAABB: " + GeometryUtility.TestPlanesAABB(frustumPlanes, bounds));
455
+ if (!GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
456
+ {
457
+ return false;
458
+ }
459
+
460
+ // Get the max bounds of the gameobject
461
+ Vector3 maxBounds = NativeUtils.GetBoundsMax(this.gameObject, mainCamera, bounds);
462
+ // Get the min bounds of the gameobject
463
+ Vector3 minBounds = NativeUtils.GetBoundsMin(this.gameObject, mainCamera, bounds);
464
+
465
+ float height = Math.Min(Screen.height, maxBounds.y) - Math.Max(0.0f, minBounds.y);
466
+ float width = Math.Min(Screen.width, maxBounds.x) - Math.Max(0.0f, minBounds.x);
467
+
468
+ // Debug.LogWarning("height: " + height);
469
+ // Debug.LogWarning("width: " + width);
470
+
471
+ // Check if the gameobject bounds are too small to be visible
472
+ if (height <= 0.01f * Screen.height || width <= 0.01f * Screen.width)
473
+ {
474
+ return false;
475
+ }
476
+ }
477
+ else // When rendered in the screen space overlay canvas
478
+ {
479
+ Debug.LogWarning("DoesUIElementOverlapScreen: " + DoesUIElementOverlapScreen());
480
+
481
+ // Check if the UI element overlaps with the screen
482
+ if (!DoesUIElementOverlapScreen())
483
+ {
484
+ return false;
485
+ }
486
+ }
487
+
488
+ return true;
489
+ }
490
+
491
+ private void CheckForImpression()
492
+ {
493
+ VisibilityInfo visibilityInfo = GetVisibilityInfo();
494
+ if (!IsImpressionChecked && IsInViewFrustum())
495
+ {
496
+ if (visibilityInfo.PercentageOfVisibleArea >= 0.5f) // if Visible Area is >= 50%
497
+ {
498
+ HasMadeImpression = true;
499
+ OnAdDisplay?.Invoke(this, EventArgs.Empty);
500
+ }
501
+ else
502
+ {
503
+ HasMadeImpression = false;
504
+ }
505
+ }
506
+ }
507
+
508
+ private float TimeInterval()
509
+ {
510
+ return 1.0f;
511
+ }
512
+
513
+ // TODO: More testing and Improvement required
514
+ // TODO: BSSDK-401 : Move viewability to container level - may not have a container
515
+ private bool IsValidUIObject()
516
+ {
517
+ Canvas canvas = GetComponentInParent<Canvas>();
518
+ if (canvas == null)
519
+ {
520
+ Debug.Log("Unable to find Canvas component in parents");
521
+ return false;
522
+ }
523
+
524
+ RectTransform rectTransform = GetComponent<RectTransform>();
525
+ if (rectTransform == null)
526
+ {
527
+ Debug.Log("RectTransform component is null");
528
+ return false;
529
+ }
530
+
531
+ CanvasRenderer canvasRenderer = GetComponent<CanvasRenderer>();
532
+ if (canvasRenderer == null)
533
+ {
534
+ Debug.Log("CanvasRenderer component is null");
535
+ return false;
536
+ }
537
+
538
+ // Rect rect = rectTransform.rect;
539
+ //
540
+ // // Get the center position of the RectTransform in its local coordinates
541
+ // Vector2 centerLocalPosition = new Vector2(rect.center.x, rect.center.y);
542
+ //
543
+ // // Convert the local center position to world position
544
+ // Vector3 centerWorldPosition = rectTransform.TransformPoint(centerLocalPosition);
545
+ // Debug.Log("centerWorldPosition: " + centerWorldPosition);
546
+ //
547
+ // Vector3 viewportPoint = NativeUtils.GetCamera().WorldToViewportPoint(centerWorldPosition);
548
+ // Debug.Log("viewportPoints: " + viewportPoint);
549
+ // if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
550
+ // {
551
+ // return true;
552
+ // }
553
+
554
+ // Check if it is completely inside the viewport
555
+ // Vector3[] corners = new Vector3[4];
556
+ // rectTransform.GetWorldCorners(corners);
557
+ // Debug.Log("Ad Object: " + this.name + " - " + corners.Length);
558
+ // foreach (Vector3 corner in corners)
559
+ // {
560
+ // // Debug.Log("corner: " + corner);
561
+ // Vector3 viewportPoint = Camera.main.WorldToViewportPoint(corner);
562
+ // // Debug.Log("viewportPoints: " + viewportPoint);
563
+ // if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
564
+ // {
565
+ // //return true;
566
+ // }
567
+ // }
568
+
569
+ // Debug.Log("Not a Valid UI Object!");
570
+
571
+ return true;
572
+ }
573
+ }
574
+ }