Category Archives for "Unity 3D"

Programming tips and tricks for Unity3D

The code used in Unity3d Gaming Subtitle Options Web App.

Screenshot ofWebGL app showing what the minimum subtitle options games should have.

Below is all the code it took to create the subtitle options in the Subtitle Options WebGl App in the previous post, Minimum Gaming Subtitle Options.

Note: this does not include the code for free version of uGUI Color Picker from the Unity Asset Store I used in the options menu. It’s not mine, so I’m not posting it.

But it should illustrate just how easy it was to create all the options shown in the Subtitle Options Web Gl App in the Minimum Gaming Subtitle Options post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using uCPf;
using System.Text.RegularExpressions;
using System.Linq;

public class ResizeCaptions : MonoBehaviour
{
  public Image captionBackground;

  public TextMeshProUGUI fontBox, shadowbox;

  public Slider fontSizeSlider, fontLineSpacingSlider, fontOpacitySlider, bgOpacitySlider;

  public Button optionButton;

  public ColorPicker font_Color_Picker, background_Color_Picker;

  public Toggle outlineToggle, shadowToggle;

  public Dropdown fontList;

  public float sidePadding = 3;  

  public TMP_FontAsset[] plainFonts, outlineFonts, outlineshadowFonts, shadowFonts;

  public float[] charSize = {10f,12.5f,10.5f};

  public float[] lineSize = {36f,62f,86f};

  [Range(1.5f,4.5f)]
  public float shadowDistance = 1.5f;

  public GameObject optionMenu;



  private int font_No;

  private GameObject shadowFontBox;

  private string[] captionSplit;
  private string myCaption;

  private RectTransform imageboxRT, shadowBoxRT;

  private Color newFontColor = new Color(1,1,1), newBackgroundColor = new Color(0,0,0), newFontColorOpacity, newBackgroundColorOpacity;

  private bool fontUpdateRequired = false, backgroudUpdateRequired = false, hasOutline = true, hasShadow = false;

  private Vector2 shadowOffset;

  private float minFontSize = 20.15f, maxCharacters, scaleUp, currentFontSize, newWidth, newHeight, fontOpacity;
  private float shadowOffsetX = 0f, shadowOffsetY = -333.1f, fontscale = 72f;

  private float[] fontsizeMinMax = { 72f, 58f, 68f };


  // Use this for initialization
  void Start ()
  {
    shadowFontBox = shadowbox.gameObject;     //sets gameobject so it can be turned on or off.

    shadowBoxRT = shadowbox.rectTransform;      //used to position dropshadow offset which is affected by font size.
    imageboxRT = captionBackground.rectTransform; //used to change size of subtitle background

    font_Color_Picker.color = newFontColor;           //sets color for color font color picker

    background_Color_Picker.color = newBackgroundColor;     //sets color for background font color

    UpdateColorPickers();                   //Late Update updates colors set on Color Pickers

    outlineToggle.onValueChanged.AddListener(Change_Outline); //Executes code when toggle is changed
    shadowToggle.onValueChanged.AddListener(Change_Shadow);   //Executes code when toggle is changed
    fontList.onValueChanged.AddListener(Change_Font);     //Executes code when dropdown is changed
    optionButton.onClick.AddListener(Check_Options);      //Executes code when button is pressed

    Change_Outline(hasOutline);                 //Turns Outline font on
    Change_Shadow(hasShadow);                 //Turns Drop Shadow off

    scaleUp = currentFontSize/minFontSize;            //gets scale of font to set background box size and drop shadow offset
    resizeBackgroundImage();                  //sets up background box size
  }


  //turns options menu on and off.
  private void Check_Options ()
  {
    if(optionMenu.activeInHierarchy == false) { optionMenu.SetActive(true); }
    else { optionMenu.SetActive(false); }
  }


  //changes font when new one is selected in Dropdown menu
  private void Change_Font (int fontNumber)
  {
    font_No = fontNumber;

    currentFontSize = fontBox.fontSize = shadowbox.fontSize = fontSizeSlider.value * fontsizeMinMax[font_No]/fontscale;

    resizeBackgroundImage();

    Change_Outline(hasOutline);   //here's where the subtitle font gets changed.
    Change_Shadow(hasShadow);   //here's where the dropshadow font gets changed.
  }


  //turns outlines on and off on subtitle
  private void Change_Outline (bool is_it_On)
  {
    if(is_it_On)
    {
      fontBox.font = outlineFonts[font_No];       //changes subtitle font
      shadowbox.font = outlineshadowFonts[font_No];   //changes dropshadow font

      fontBox.characterSpacing = shadowbox.characterSpacing = 0.25f;    //increases character spacing if outlines are on for easier readding.

      hasOutline = true;
    }
    else
    {
      fontBox.font = plainFonts[font_No];         //changes subtitle font
      shadowbox.font = shadowFonts[font_No];        //changes dropshadow font

      fontBox.characterSpacing = shadowbox.characterSpacing = 0f;     //removes extra character spacing if outlines are off.

      hasOutline = false;
    }
  }


  //turns Shadows on and off on dropshadow font.
  private void Change_Shadow (bool is_it_On)
  {
    if(is_it_On)
    {
      if(hasOutline) { shadowbox.font = outlineshadowFonts[font_No]; }
      else { shadowbox.font = shadowFonts[font_No]; }

      Fix_ShadowOffset();

      shadowFontBox.SetActive(true);
      hasShadow = true;
    }
    else
    {
      if(hasOutline) { shadowbox.font = outlineshadowFonts[font_No]; }
      else
      {
        shadowbox.font = shadowFonts[font_No];
      }

      shadowFontBox.SetActive(false);
      hasShadow = false;
    }
  }


  //changes drop shadow offset amount when font size changes
  private void Fix_ShadowOffset ()
  {
    shadowOffset = new Vector2(shadowOffsetX + shadowDistance * scaleUp, shadowOffsetY - shadowDistance * scaleUp);
    shadowBoxRT.anchoredPosition = shadowOffset;
  }


  //Updates Color Picker UI
  public void UpdateColorPickers()
  {
    fontUpdateRequired = true;
    backgroudUpdateRequired = true;
  }


  //changes subtitle font color
  public void FontColorChanged (Color passed_Color)
  {
    newFontColorOpacity = passed_Color;
    newFontColorOpacity.a = fontOpacitySlider.value;
    fontBox.color = newFontColorOpacity;
    fontUpdateRequired = true;
  }


  //changes background color
  public void BackgroundColorChanged (Color passed_Color)
  {
    newBackgroundColorOpacity = passed_Color;
    newBackgroundColorOpacity.a = bgOpacitySlider.value;
    captionBackground.color = newBackgroundColorOpacity;
    backgroudUpdateRequired = true;
  }


  //changes subtitle font opacity
  public void FontOpacityChanged ()
  {
    newFontColorOpacity.a = fontOpacitySlider.value;
    fontBox.color = newFontColorOpacity;
  }


  //changes background opacity
  public void BackgroundOpacityChanged ()
  {
    newBackgroundColorOpacity.a = bgOpacitySlider.value;
    captionBackground.color = newBackgroundColorOpacity;
  }


  //Updates Color Pickers
  void LateUpdate()
  {
    if (fontUpdateRequired) { font_Color_Picker.UpdateUI (); fontUpdateRequired = false; }
    if (backgroudUpdateRequired) { background_Color_Picker.UpdateUI (); backgroudUpdateRequired = false; }
  }


  //changes Line Spacing in subtitle and and dropshadows
  public void Change_LineSpacing ()
  {
    fontBox.lineSpacing = shadowbox.lineSpacing = fontLineSpacingSlider.value;
    resizeBackgroundImage ();
  }


  public void resizeBackgroundImage ()
  {
    myCaption = fontBox.text;     //Gets Caption Text

    //sets new font size
    currentFontSize = fontBox.fontSize = shadowbox.fontSize = fontSizeSlider.value * fontsizeMinMax[font_No]/fontscale;

    //gets scale of font to set background box size and drop shadow offset
    scaleUp = currentFontSize/minFontSize;

    if(myCaption == null)           //checks to make sure there is a caption.
    {
      //Debug.Log ("It's null");
      captionBackground.enabled = false;    //turns off caption backgrund if there's no image.
      return;                 //returns if there is no caption.
    }

    if(!captionBackground.enabled) { captionBackground.enabled = true; }  //turns it back on if a caption replaces a blank caption.
     
    captionSplit = myCaption.Split(new string[]{ "\n" }, System.StringSplitOptions.RemoveEmptyEntries); //splits caption into separate lines.
    maxCharacters = 0f;

    for (int i = 0; i < captionSplit.Length; i++)   //we need to get the length of the longest ling of captions to set background box size
    {
      if(captionSplit[i].Length > maxCharacters)
      {
        maxCharacters = captionSplit[i].Length;   //gets length of longest line for setting box width.
      }
    }

    maxCharacters += sidePadding;               //adds padding at side for background box.

    newWidth = maxCharacters * charSize[font_No] * scaleUp;   //gets new width based on font parameters for background box.

    //gets new height based on font parameters for background box.
    newHeight = (lineSize[captionSplit.Length-1] * scaleUp) + (fontLineSpacingSlider.value/110 * lineSize[captionSplit.Length-1]);

    imageboxRT.sizeDelta = new Vector2(newWidth, newHeight);  //sets size of background box.

    Fix_ShadowOffset();                     //Fixes size of dropshadow offset for new font size.
  }
}

Minimum Gaming Subtitle Options

Screenshot ofWebGL app showing what the minimum subtitle options games should have.

After talking with a number of people on Twitter, and given the awful, or limited gaming subtitle options for PC and console games, I decided to set up an app with the minimum subtitle options games should have.

I used Unity3d to create the WebGL app. You may need to install the Unity3d Web Player plugin available for Windows and Mac browsers.

The background image and subtitle is from Wolfenstein: The New Order. The starting subtitle size is approximately the same size as it appears in game, but I’ve split the subtitle into two lines. The original was on one line, breaking one of the primary closed captioning/subtitle guidelines of no more than 42-45 characters per line.

If there had been options to increase the size of the subtitles in the game, you wouldn’t have been able to make them much bigger without the caption getting cut off on either side of the screen. Splitting the caption to proper lengths gives you room to make subtitles much bigger.

Some people may think subtitles cover up too much of a game screen, but that’s irrelevant. The whole point of subtitles is communication. If players can’t read them, they will miss vital gameplay information and story.

Making subtitles readable to a wide range of players is what’s key with subtitle options. Some people don’t need subtitles at all. some like small subtitles. Others need high contrast between subtitles and backgrounds. Some have low vision and need them as large as possible.

Game companies need to expand the range of options available for subtitles. I’ve set what I think should be the minimum standard for subtitle options for games.

One other important thing about subtitle options – the player should be able to see exactly what they’re getting in the subtitle options with a full screen sample. Having a small sample image with subtitles on them to illustrate is not enough. The sample should be full screen right in the options menu so players can set subtitles up exactly the way they need to so they can read them as easily as possible.

Options include, font size, sans-serif, serif, and monospace fonts, background, font transparency, background transparency, font color, background color, outline, and drop shadow.

To start, click the Options button in the top right corner in the WebGl app below.

For a fantastic article on gaming subtitles by Max Deryagin, check out on “What Video Game Subtitling Got Wrong In 2017

How to Add ‘Apply Changes to Prefab’ to right-click menu in Unity3D

 

It’s extra steps and work to keep going to the GameObject window in Unity3d to select ‘Apply Changes to Prefab’ after selecting the prefab. Now you can do it just by right-clicking the prefab and selecting ‘Apply Changes to Prefab’ from the context menu. Just download the editor file here and add it to a folder named Editor (as seen in project folder in the video).

Using AnimationCurves in Unity3D

Another use for AnimationCurves in Unity3D is to adjust the speed of things without changing variables, or adding complicated curves. The variable we want returned is the Y-Point (a float from 0.0 to 4.0), based on time (from 0.0 to 1.0, going left to right).

As time gets closer to 1.0, the Y-point decreases from 4.0 to 1.0.


1
powerbarReverse.fillAmount += Time.deltaTime / powerCurveStart.Evaluate (powerbarReverse.fillAmount);

powerbarReverse is a UI Image whose image type is filled. As you change the fill amount from 0.0 to 1.0, the image fills in, in the Fill Origin type specified in the inspector.

powerCurveStart is the Animation curve below. As time goes on, the image will fill faster, as Time.delta time gets divided by a smaller number.

AnimationCurve used when the Power Bar is moving forward (increasing).

AnimationCurve used when the Power Bar is moving forward (increasing).

 


1
powerbarReverse.fillAmount -= Time.deltaTime / powerCurveEnd.Evaluate (powerbarReverse.fillAmount);

powerCurveEnd is the Animation curve below. It will start at 1.0, and the image will unfill as time goes on. It starts fast, and slows down. When it reaches 0.33 is the speed will stay constant. The speed stays slow and constant at this point so players can decide if they want to add spin to the bowling ball to make it hook left or right.

AnimationCurve used when the Power Bar is reversing (decreasing).

AnimationCurve used when the Power Bar is reversing (decreasing).

 

To add spin or hook the ball, the player would stop the power bar on either side of the “bump” in the left side of the power bar meter. If the player stops it where shown below, the ball will have no spin and roll perfectly straight down the alley.

Power Bar Meter

Sample code is below. A UI.Button calls the SwingClub() routine, which starts, reverses and stops the power bar with each press.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<span style="font-family: Menlo;"><span style="color: #009695;"><span style="font-family: Menlo;"><span style="color: #009695;">private</span><span style="color: #333333;"> </span><span style="color: #009695;">int</span><span style="color: #333333;"> </span><span style="color: #333333;">pbDirection</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #f57d00;">0</span><span style="color: #333333;">;</span></span>
public</span><span style="color: #333333;"> </span><span style="color: #3364a4;">Image</span><span style="color: #333333;"> </span><span style="color: #333333;">powerbarForward</span><span style="color: #333333;">;</span>
<span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #3364a4;">Image</span><span style="color: #333333;"> </span><span style="color: #333333;">powerbarReverse</span><span style="color: #333333;">;</span></span>
<span style="font-family: Menlo;"><span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #3364a4;">AnimationCurve</span><span style="color: #333333;"> </span><span style="color: #333333;">powerCurveEnd</span><span style="color: #333333;">;</span>
<span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #3364a4;">AnimationCurve</span><span style="color: #333333;"> </span><span style="color: #333333;">powerCurveStart</span><span style="color: #333333;">;</span></span>
<span style="font-family: Menlo;"><span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #009695;">enum</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;"> </span><span style="color: #333333;">{</span><span style="color: #333333;"> swingInactive</span><span style="color: #333333;">,</span><span style="color: #333333;"> startSwing</span><span style="color: #333333;">,</span><span style="color: #333333;"> stopSwing</span><span style="color: #333333;">,</span><span style="color: #333333;"> endSwing</span><span style="color: #333333;">,</span><span style="color: #333333;"> BallThrown </span><span style="color: #333333;">}</span><span style="color: #333333;">;</span>
<span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;"> </span><span style="color: #333333;">actionstate</span><span style="color: #333333;">;

<span style="font-family: Menlo;"><span style="color: #009695;">public</span><span style="color: #333333;"> </span><span style="color: #009695;">void</span><span style="color: #333333;"> </span><span style="color: #333333;">SwingClub</span><span style="color: #333333;">()</span>
<span style="color: #333333;">{</span>
 <span style="color: #333333;">    </span><span style="color: #009695;">switch</span><span style="color: #333333;">(</span><span style="color: #333333;">actionstate</span><span style="color: #333333;">)</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">{</span>
 <span style="color: #333333;">        </span><span style="color: #009695;">case</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">swingInactive</span><span style="color: #333333;">:</span><span style="color: #333333;"> </span><span style="color: #333333;">StartSwing</span><span style="color: #333333;">()</span><span style="color: #333333;">;</span><span style="color: #333333;"> </span><span style="color: #009695;">break</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">        </span><span style="color: #009695;">case</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">startSwing</span><span style="color: #333333;">:</span><span style="color: #333333;"> </span><span style="color: #333333;">StopSwing</span><span style="color: #333333;">()</span><span style="color: #333333;">;</span><span style="color: #333333;"> </span><span style="color: #009695;">break</span><span style="color: #333333;">;</span><span style="color: #333333;">     </span>
 <span style="color: #333333;">        </span><span style="color: #009695;">case</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">stopSwing</span><span style="color: #333333;">:</span><span style="color: #333333;"> </span><span style="color: #333333;">EndSwing</span><span style="color: #333333;">()</span><span style="color: #333333;">;</span><span style="color: #009695;">break</span><span style="color: #333333;">;</span><span style="color: #333333;">    </span>
 <span style="color: #333333;">    </span><span style="color: #333333;">}</span>
 <span style="color: #333333;">}</span>
 
 <span style="color: #009695;">void</span><span style="color: #333333;"> </span><span style="color: #333333;">StartSwing</span><span style="color: #333333;">()</span>
 <span style="color: #333333;">{</span>
 <span style="color: #333333;">    </span><span style="color: #009695;">if</span><span style="color: #333333;">(</span><span style="color: #333333;">playerReadyButtonActive</span><span style="color: #333333;">)</span><span style="color: #333333;"> </span><span style="color: #333333;">{</span><span style="color: #333333;"> </span><span style="color: #009695;">return</span><span style="color: #333333;">;</span><span style="color: #333333;"> </span><span style="color: #333333;">}</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">actionstate</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">startSwing</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">pbDirection</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #f57d00;">1</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">StartCoroutine</span><span style="color: #333333;"> </span><span style="color: #333333;">(</span><span style="color: #333333;">MovePowerBar</span><span style="color: #333333;">())</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">}</span>
 
 <span style="color: #009695;">void</span><span style="color: #333333;"> </span><span style="color: #333333;">StopSwing</span><span style="color: #333333;">()</span>
 <span style="color: #333333;">{</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">powerbarForward</span><span style="color: #333333;">.</span><span style="color: #333333;">fillAmount</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #333333;">powerbarReverse</span><span style="color: #333333;">.</span><span style="color: #333333;">fillAmount</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">pbDirection</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #333333;">-</span><span style="color: #f57d00;">1</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">actionstate</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">stopSwing</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">}</span>
 
<span style="color: #009695;">void</span><span style="color: #333333;"> </span><span style="color: #333333;">EndSwing</span><span style="color: #333333;">()
</span><span style="color: #333333;">{</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">pbDirection</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #f57d00;">0</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">    </span><span style="color: #333333;">actionstate</span><span style="color: #333333;"> </span><span style="color: #333333;">=</span><span style="color: #333333;"> </span><span style="color: #3364a4;">swingType</span><span style="color: #333333;">.</span><span style="color: #333333;">endSwing</span><span style="color: #333333;">;</span>
 <span style="color: #333333;">}</span>
 </span> </span></span>
IEnumerator MovePowerBar ()
1
{
1
    while (pbDirection != 0)
1
    {
1
         if (pbDirection == 1)
1
         {
1
          powerbarReverse.fillAmount += Time.deltaTime / powerCurveStart.Evaluate (powerbarReverse.fillAmount);
1
             if (powerbarReverse.fillAmount &gt;= 1.0f)
1
             {
1
                 pbDirection = -1;
1
                 powerbarForward.fillAmount = 1f;
1
                 actionstate = swingType.stopSwing;
1
             }
1
         }
1
         else
1
         {
1
             powerbarReverse.fillAmount -= Time.deltaTime / powerCurveEnd.Evaluate (powerbarReverse.fillAmount);
1
             if (powerbarReverse.fillAmount &lt;= 0f)
1
             {
1
                 pbDirection = 0;
1
                 actionstate = swingType.endSwing;
1
             }
1
         }
1
         yield return null;
1
     }
1
 }

 

Unity3D: Direct Function calling vs SendMessage

Comparing calling a function directly in a script vs using SendMessage to a Game Object.

Two counters start simultaneously, counting to 1,000 and sending the int ‘1’ to a function in another script.

SendMessage uses:
counterGO.SendMessage(countem1);

Direct Function uses:
crScript.countem(1);

SendMessage takes just over twice as long to perform the same feat as Direct Function (209%).

 

SendMessage has an advantage in that you don’t need to know the name of the script on the GameObject you’re sending the message to. Direct Function needs to know the name of the script.

Aside from being faster, Direct Function can also send more than one parameter to a function in another script, so you could send a string, float, int, Vector2, Vector3 and more in one single code, provide the function you’re sending them to can receive those parameters.

i.e:

crScript.countem(13.509Quote thisVector2(1.0,2.0), Vector3(2.5,3.5,4.5));

 

You can find the example project from the video above here: SendMessage vs Direct Function