Floating texts in Unity - c#

My 2D platformer game level has treasure chests placed all over the map and when a chest is collected I need to display a message. The messages are contained in a List<string> and they are displayed one by one as the treasures are collected.
These messages are to be displayed in a UI>Text gameObject which is anchored to the top-center of the canvas. I want to display these texts as floating up(fading in/out) when the treasures are collected, by updating the text component of this gameObject. However, the problem arises when two or more treasures are collected before the animation for the previous one could be complete. I can easily concatenate the new messages to the existing ones, but I want the old ones to fade out and new ones to fade in. This can be done by creating multiple UI>Texts, but there are a lot of messages and I do not want to create so many redundant gameobjects. Is there any good workaround for this problem?

The way I handled this in a project of mine was to create a queue of messages to display (as immediacy was not a concern, but being able to only display one at a time was). This sounds very similar to your own problem.
// NotificationItem is just a wrapper around some text and accompanying image
private static List<NotificationItem> notificationQueue = new List<NotificationItem>();
// reference to the on-screen object
public GameObject notification;
// "Hey! I want to display a notification!"
public static void ShowNotification(NotificationItem item) {
notificationQueue.Add(item);
}
// I was using the DO Tween asset here, but the logic can be converted to
// coroutines or straight update cycles
private void Update() {
// If there is no currently displayed notification (ie the notification object is
// not being animated) and there is at least one item to display
if(!DOTween.IsTweening(notification.transform) && notificationQueue.Count > 0) {
// ...get the first one
NotificationItem item = notificationQueue[0];
// ...pop it from the list
notificationQueue.RemoveAt(0);
// ...set the notification object to the details
notification.transform.Find("Title").GetComponent<Text>().text = item.title;
notification.transform.Find("Text").GetComponent<Text>().text = item.text;
notification.transform.Find("Img").GetComponent<Image>().sprite = item.image;
// ...start the animation
// (in my case, the notification animates down from the top of the screen
// waits 2.5 seconds, then animates back up)
notification.transform.DOMoveY(Screen.height - 85, 0.5f, false).SetEase(Ease.InOutQuad).OnComplete(PauseCallback);
}
}
// An admittedly hacky way of getting the notification to do nothing for 2.5 seconds:
// Animate it to where it already is.
private void PauseCallback() {
notification.transform.DOMoveY(Screen.height - 85, 2.5f, false).SetEase(Ease.InOutQuad).OnComplete(ReturnCallback);
}
private void ReturnCallback() {
notification.transform.DOMoveY(Screen.height + 2, 0.5f, false).SetEase(Ease.InOutQuad);
}
The difference between my implementation and yours will be largely in the animation (as well as your Queue lists's type; e.g. you might be able to just use a List<string>). You already have your animation coded, all you need is the queue and a way to determine that your animation is complete.

You will not be generating more objects than the system can handle if you utilize the Flyweight pattern (object pooling). Unity has an Object Pooling tutorial on their site.

Related

How to Handle ARAnchor merge/replacement in the Unity ARKit Plugin

I'm trying to run an ARKit session in Unity (using the Unity ARKit Plugin), where the user selects a plane at the start of the session, and we build a world around that plane.
Obviously, ARKit's estimate of the plane updates as time goes on, and the application handles that through registering against the UnityARSessionNativeInterface.ARAnchorUpdatedEvent and manipulates the game world accordingly; however when a plane is merged into another, I'm struggling to find a way to capture it so we can update with the new plane.
In ARCore, we are provided the "subsumedBy" field, when one plane is merged into another. We can then use this to trigger a change of planes by registering against an event:
public static Action<BoundedPlane,BoundedPlane> planeReplaced;
// ...
public override void Update()
{
Session.GetTrackables<DetectedPlane>(m_DetectedPlaneBuffer, TrackableQueryFilter.All);
foreach (var detectedPlane in m_DetectedPlaneBuffer)
{
// ...
if (detectedPlane.SubsumedBy != null)
{
OnPlaneReplaced(boundedPlane, detectedPlane.SubsumedBy);
}
// ...
}
}
I assume the Unity plugin doesn't support this behaviour out of the box, so starting looking at the Objective-C native session implementation for clues. The closest I can see is the [session:didUpdateAnchors][3] delegate (and associated add/remove ones), but that doesn't seem to provide information on merges.
The worst case is that I can look for the most similar anchor in the event my chosen one is removed, but that seems hacky, so I wondered if there might be a better way.
ARKit plug in has a removed anchor event. Use that in conjunction with recently added anchors to find the closest anchor to the one removed:
void Start () {
UnityARSessionNativeInterface.ARAnchorAddedEvent += ARAnchorAdded;
UnityARSessionNativeInterface.ARAnchorRemovedEvent += ARAnchorRemoved;
}
void ARAnchorAdded(ARPlaneAnchor anchorData)
{
//store anchorData in an array, you know the latest anchor is the newest.
}
void ARAnchorRemoved(ARPlaneAnchor anchorData)
{
//find the closest anchor, start at the bottom if you are looking for newest first
//remove anchorData from the array
}
Unity no longer supports the ARKit plug in, it is recommended to use the AR Foundation: https://docs.unity3d.com/Packages/com.unity.xr.arfoundation#1.0/manual/index.html

How to queue up actions and animations in Unity for turn-based combat?

I'm working on a turn-based combat sequence in Unity on C# and I currently have turn switching working and inputs and AI. But I can't quite figure out how to queue it up with animations anf stuff, so that one happens after another in ExecuteTurnActions();
So my question is basically, how do I structure ExecuteTurnActions()?
I need it to go through all Entities and play their animations according to what's going on, like:
// PlayAnimation is simply Animator.SetTrigger(string)
Entities[0].PlayAnimation("Attack") or Entities[0].PlayAnimation("ReceiveDamage")
I need to make sure that when one Entity attacks, right when the attack happens, the other Entity receives damage and plays its animation at that moment. I know there are animation events, but I'm not sure how to make it flow from one place and make sure each entity knows which entity to trigger for animation next (when attacked for example). Any suggestions?
My structure looks something like this in pseudocode:
ENTITIES_TO_CREATE = MatchMaker.GetNumberOfPlayersAndEnemies();
void Start() {
InitializeAllThatsNeeded();
Entities[] = new Entity[ENTITIES_TO_CREATE];
// In a for loop, load up stuff and populate Entities
Entities[0].Name = MatchMaker.GetName(0);
Entities[0].Strength = MatchMaker.GetStrength(0); // etc etc etc
}
void Update() {
bool HaveAllEntitiesSelectedTurnAction = new bool[Entities.Length];
// Check every frame if all Entities have selected a turn action
for (int i = 0; i <= Entities.Length - 1; i++) {
HaveAllEntitiesSelectedTurnAction[i] = Entities[i].isTurnActionSelected;
}
// If the Entity that goes first has its action selected, then let the rest select theirs
if(Entities[EntityHasFirstTurn].TurnActionSelected) {
//All logic happens here, if Entity happens to be player then the player
//is allowed input, if enity is NPC then generate hit value etc.
// isTurnActionSelected set in this part
// Now if all have selected their action, execute them in order
if(HaveAllEntitiesSelectedTurnAction.All(x => x) {
ExecuteTurnActions();
}
}
}
To prioritize events I would suggest using a priority queue data structure.
As for ExecuteTurnActions(), it should only set appropriate flags of an Animator Component (based on the queue, most likely). You might want each entity to have its own event queue which interacts with its own Animator.
Building a state machine for all your animations is what you need to do first. Setting certain states will allow Unity to handle all the transitions. To learn more about the Animator and handling its states look here.

Unity3D: Run-time generated AnimationClips leaking

I currently create AnimationClips dynamically while my game is running to move some pieces around. You can see what I mean here: https://youtu.be/u1My9JX4K-c?t=8s , when the pieces get grouped together and then open again. I cannot reuse the clip because the pieces will have a different position next time, so all the movements need to be changed.
Right now, I create a new clip whenever I need them to be grouped and separated, and attempt to replace any existing clip with this:
private void ReplaceAndPlay(AnimationClip clip, string name) {
var old = Anim.GetClip(name);
if (old != null) {
Anim.RemoveClip(name);
Destroy(old);
}
Anim.AddClip(clip, name);
Anim.Play(name);
}
(Anim just points to the gameObject's Animation component)
But using the profiler, I see that the animation clips just keep accumulating, they don't seem to be garbage collected. Am I missing something or doing something wrong? Can they be garbage collected?

C# Simple 2d game - making the basic game loop

Even though i have some experience in c#, this is my First game in C#. I am trying to set up the minimal skeleton of the game. I heard that Tick Event is a bad approarch for creating the main game loop.
This is the main concept of what I am trying to implement:
Program.cs
//Program.cs calls the Game Form.
Application.Run(new Game());
Game.cs
public partial class Game : Form
{
int TotalFramesCount = 0;
int TotalTimeElapsedInSeconds = 0;
public Game()
{
InitializeComponent();
GameStart();
}
public void GameStart()
{
GameInitialize();
while(true)
{
GameUpdate();
TotalFramesCount++;
CalculateTotalTimeElapsedInSeconds();
//Have a label to display FPS
label1.text = TotalFramesCount/TotalTimeElapsedInSeconds;
}
}
private void GameInitialize()
{
//Initializes variables to create the First frame.
}
private void GameUpdate()
{
// Creates the Next frame by making changes to the Previous frame
// depending on users inputs.
}
private void CalculateTotalTimeElapsedInSeconds()
{
// Calculates total time elapsed since program started
// so that i can calculate the FPS.
}
}
Now, this will not work because the while(true) loop blocks the Game Form from initializing. I found some solutions to this, by using System.Threading.Thread.Sleep(10); or Application.DoEvents();, but I didn't manage to make it work.
To explain why I want to implement this code here is an example of the above code in use:
Lets say I want my game to do the following:
Smoothly move a 100x100 Black colored Square from point (x1,y1) to (x2,y2) and backwards, in a loop and display the FPS in the label1 of the above code. With the above code in mind, I could possibly use TotalTimeElapsedInSeconds variable to set the speed of the movement to be relevant with the Time and not the Frames, as the Frames will differ on each machine.
// Example of fake code that moves a sqare on x axis with 20 pixels per second speed
private void GameUpdate()
{
int speed = 20;
MySquare.X = speed * TotalTimeElapsedInSeconds;
}
The reason i though of using a while(true) loop is that I will get the best FPS I can on each machine.
How could I implement my idea on actual code ? (just the basic skeleton is what i am looking for)
How could I set a max of, lets say 500 FPS to make the code "lighter" to run? instead of trying to produce as many frames as possible which I suspect will needlesly over-use the CPU(?)
Frame rate has nothing to do with smoothness. Even if you accomplish 500 frames/sec the movement will be choppy or worse. The trick is to synchronize with your monitor refresh rate. So for a monitor with 60Hz you need 60 frames/sec no more no less. You can't do that by using a loop in C#. You need DirectX or XNA. These frameworks can synchronize your drawings with the vertical scan of your monitor.
You need to make own thread to that while(true)-loop:
Thread thread = new Thread(new ThreadStart(GameStart));
thread.Priority = ThreadPriority.Lowest;
InitializeComponent();
thread.Start();
Check this blog post to get more coding intuition:
https://praybook2.blogspot.com/2020/04/this-now-advanced-stuff.html
Tough it loop fast. There are many disadvantages by using the threads, consider using some ready built game engine --like Godot; where all these kinds of small problems are fixed beforehand, use threads only when they are needed.

C# Game Programming: Is it better to save or calculate non-changing datum?

I'm programming a game in the C# XNA framework and something has been bugging me for quite some time. I'm never certain whether it's to the advantage of a program to save data (that is to say, store it within a local variable) or to calculate it each frame.
While I've decided that it's better to continuously calculate data that changes each frame (such as the position of the player, the positions of automated sprites, et cetera), I'm uncertain whether or not I should save a piece of data like the width of a window or calculate it each frame. My fear is that calculating a piece of data, regardless of how small it may be, sixty times every second will generate quite a bit of overhead, so thus far I've been sticking to saving a lot of material.
Here's an example of what I'm trying to get across:
// Scenario 1: Save the data
// This class represents an entry within a menu.
public class MenuEntry
{
// The constructor takes a SpriteFont variable to calculate the size of
// the given string (which is the text used to represent this entry in a
// menu) and save that data for later access
public MenuEntry(string entryText, SpriteFont font)
{
this.entryText = entryText;
// Initializes a local value of the font to lessen the burden of method calls.
SpriteFont font = menuScreen.GameInstance.MenuFont;
// Initialize the bounding rectangle for this 'MenuEntry' object.
entryRectangle = new Rectangle();
// Based on the text given, adjust the values of the rectangle.
entryRectangle.Height = font.LineSpacing;
entryRectangle.Width = (int)font.MeasureString(entryText).X;
}
// Object methods
// This method simply sets the location of the 'hitbox' of this MenuEntry based
// on the given position.
public void SetLocation(int x, int y)
{
entryRectangle.X = x;
entryRectangle.Y = y;
}
// Object fields
// Rectangle that represents the 'hitbox' for this MenuEntry. In other
// words, the place on the screen over which the mouse can hover to cause
// this MenuEntry to be the selected item on the menu
private Rectangle entryRectangle;
// The 'entryText' is the string representation of this MenuEntry object in
// a menu.
private string entryText;
}
// Scenario 2: Calculate per frame
// This class also represents an entry within a menu.
public class MenuEntry
{
// The constructor simply initializes the value of the text field
public MenuEntry(string entryText)
{
this.entryText = entryText;
}
// Object methods
// Instead of saving the data, these methods allow this MenuEntry
// to calculate its 'hitbox' whenever this method is called,
// returning the width and height variables associated with the
// text representation of this MenuEntry object.
public virtual int GetWidth(SpriteFont font)
{
return (int)font.MeasureString(Text).X;
}
public virtual int GetHeight(SpriteFont font)
{
return font.LineSpacing;
}
// Object fields
// The MenuEntry still must know where it's located, but this component
// is less memory intensive than a 'Rectangle' object (correct me if
// I'm wrong on this one)
private Vector2 position;
// This property allows for the controlling menu to place and replace the
// MenuEntry, though this value must be calculated each frame even though
// the position may be the same for each frame.
public Vector2 Position
{
get { return position; }
set { position = value; }
}
// The 'entryText' is the string representation of this MenuEntry object in
// a menu.
private string entryText;
}
// Example in menu:
public class Menu
{
// For the sake of simplicity, the constructor simply receives a list of entries
// and initializes them to a local variable. If we're using Scenario 1, the
// positions for each rectangle must be set.
public class Menu(SpriteFont menuFont, List<MenuEntry> menuEntries)
{
this.menuFont = menuFont;
this.menuEntries = menuEntries;
#if Scenario 1
Vector2 position = new Vector2(0f, 175f);
for(int i = 0; i < menuEntries.Count; i++)
{
// Initialize a local reference to the current MenuEntry.
MenuEntry menuEntry = menuEntries[i];
// Adjust this entry toward the center of the screen. Assume it
// has access to a static viewport variable.
position.X = Game.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
// Set the entry's position to the calculated position. There must be
// casts as these are floating-point values.
menuEntry.SetLocation((int)position.X, (int)position.Y);
// Move down for the next entry the size of this entry
position.Y += menuEntry.GetHeight(this);
}
#endif
}
// This method is called by the main update method once per frame.
public void Update(GameTime gameTime)
{
#if Scenario 1
// Nothing, as the MenuEntry items don't need to move and their positions
// have already been set.
#elif Scenario 2
Vector2 position = new Vector2(0f, 175f);
for(int i = 0; i < menuEntries.Count; i++)
{
// Initialize a local reference to the current MenuEntry.
MenuEntry menuEntry = menuEntries[i];
// Adjust this entry toward the center of the screen. Assume it
// has access to a static viewport variable.
position.X = Game.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
// Set the entry's position
menuEntry.Position = position;
// Move down for the next entry the size of this entry
position.Y += menuEntry.GetHeight(this);
}
#endif
}
// Object fields
// The list of MenuEntry items associated with this Menu.
private List<MenuEntry> menuEntries;
// The font used for this Menu.
private SpriteFont menuFont;
}
Though the example is a bit long, I feel as though it adequately encapsulates my question. So, is it better to save more data to an object (in the case of the example, save a rectangle structure) or is it better to calculate this data each frame? Note that the data I'm speaking of doesn't change between frames.
60 times per second in nothing for simple calculations. Your computer can do several million simple calculations per second.
Chose the one that produces the easier to maintain code. And since windows can be resized I strongly recommend recalculating window based stuff like aspect ratio, projection matrix etc on each frame.
Such performance concerns are only relevant for code that gets executed very often. Say >1 million times a second.
For rare execution only expensive calls such as IO matter.
So I'm with svick on this issue: Profile to find the bottleneck and then optimize it. For all other code use the clearest simplest version.
More memory usage or slower performance. That's what it boils down to. Which is more critical in your case?
Do whatever makes for the easiest to read and understand code. You should only start worrying about optimisation if you have an actual performance problem.
Code to handle things like menu layout is never going to require optimisation - certainly at the level of micro-optimisations like: how big certain data types are, the overhead of method calls, and whether you need to cache results.
Start worrying about that when you're making a 100+ object physics simulation. Or a 10000+ object particle system. Or a 100+ polygon collision-detection routine.
Puting this kind of effort into a 10+ entry menu is insane, and that makes it difficult to give any actual performance advice.
In many cases caching data can actually reduce performance, because the CPU can do some fairly complicated calculations in the time it takes to read and write data from memory. The heaviest thing in your code is SpriteFont.MeasureString - and even that is probably "fast enough" in your situation.
Mentioning memory access patterns is also worthwhile - in cases where you don't have a large number of data items, the size of each individual item doesn't matter much (eg: Rectangle vs Vector2, strings) because memory is fetched into the CPU cache in much larger blocks than that.
I can't stress enough how bad of an idea it is to be optimising this at all! Just write nice, easy-to-read code.
Personally I'd be focusing more on using the CLR Profiler to ensure you're not allocating memory during your draw-update cycle (causing the garbage collector to run intermittently), rather than worrying about low-level CPU and memory-access-time stuff.
I don't think you have much choice, every frame you should update all of your moving objects.

Categories