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

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

Related

Hide and show a button in Unity (C#)

I have a button (in UI), that I want to hide sometimes (but show again later), which means that it shouldn't show anymore and I shouldn't be able to click it. The only solution I found (that has actually managed to hide the button), is SetActive().
But in Update(), when I do SetActive(false) (the true/false is controlled with the variable wave_happening in my script), Update() doesn't run anymore, so I can't set it to true again.
When I do GameObject.Find("Start Button").SetActive(true) in another script, it just gives me a NullReferenceException (Object reference not set to an instance of an object).
This is my Update() function:
void Update() {
wave_happening = enemy_spawner_script.wave_happening;
Debug.Log(wave_happening);
transform.gameObject.SetActive(wave_happening);
}
Is there a solution to stop this problem, or another way to hide a button?
I'm fairly new to Unity and C#, so I don't know very much.
You can try disabling the button's rendering and functionality components:
GetComponent<Button>().enabled = false; // remove functionality
GetComponent<Image>().enabled = false; // remove rendering
Now, adding that to your Update function, plus a few changes for performance so you are not enabling/disabling every single frame, only when needed:
private bool isShowing = true; // or whatever your default value is
void Update() {
wave_happening = enemy_spawner_script.wave_happening;
Debug.Log(wave_happening);
if(wave_happening != isShowing) {
show(wave_happening);
isShowing = wave_happening;
}
}
void show(bool isShow) {
GetComponent<Button>().enabled = isShow; // remove functionality
GetComponent<Image>().enabled = isShow; // remove rendering
}
It's really difficult to do this well. Lots of issues arise:
Inevitably the button will be in a H or V layout group, and, as a basic software engineering issue of course you want it to work whether or not it is in a layout group, as that layout detail may be changed by your designers as the project goes on. For this reason it is a really good idea, as the OP initially guessed, to simply use .SetActive
But then you have the problem of the button being off so Update is not running. A simple solution is just to put the button in a wrapper. That is to say, simply in a UI Panel. Have the button manager script on that wrapper rather than on the button per se.
Then you just have a pretty Property on the manager script,
public bool Showme
{
etc...
and then you can just go ...
void Update()
{
Showme = enemy_spawner_script.wave_happening;
}
Can't get simpler looking code.
However, if you use a wrapper Panel. It is true that you have to be pretty expert at using the UI (particularly the auto sizing stuff) to make it work just the way you want. But, that's part of building Unity expertise unfortunately. :/
The concerns about performance is ... truly ridiculous. There's no difference between Unity's raw code "checking a boolean" and yourself "checking a boolean". However for sure as a matter of style it's crap to poll it in Update. (Note that in point "B" just above, if you're going to have heinously ugly "anti-polling" code, bury it in that Property.)
And then ...
"Trick" solution
A tip in Unity UI is you can add a CanvasGroup anywhere. Why would you do this? It allows you to FADE the whole thing, which, is often a quick solution to achieve what you want.
Hence ..
public CanvasGroup fader;
and then ..
void Update()
{
fader.alpha = enemy_spawner_script.wave_happening ? 1f : 0f;
}
and you're done!

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.

Emitting a constant force from an object(Leaf blower Mechanic) C#

I am just about to tackle a new project where the core game mechanic is covering up dead bodies killed by the mafia with leaves using a leafblower.
You need to cover them well enough to not draw attention when the police question you.
The only problem I can see with this project is how to create this leafblower mechanic.
Off the top of my head I can think of attaching an empty object to my leaf blower which has a constant [rigibody.AddExplosiveForce] that procs on mouse click but thats it.
What should I be looking at and what would be a logical way to create this? You do not need to write the whole code for me, I just wan't to know what tools or scripting API I need to consider.
Thank you :)
You could try continuous adding force using Rigidbody.AddForceAtPosition(). Or calculate directions and try Rigidbody.AddForce().
Basically Rigidbody.AddExplosionForce is working as Rigidbody.AddForce, but uses sphere to calculate Vector3. If you need to emit it continuously, you should just use it in FixedUpdate() and list of object you can get by using Raycasts or simple Trigger Collider, for example Capsule Collider.
You could use Collider Capsule like this ↑↑↑. And in OnTriggerEnter or OnCollisionEnter:
private void OnCollisionEnter(Collider other) {
if (other.gameobject.tag != "leaf") return;
// Your AddForce here.
// Also you can get exact point of collsion by using
// other.contacts and use this as AddForce argument.
}

Floating texts in Unity

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.

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?

Categories