Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Question How to implement Attack Interval? by Awaitable Class?

Discussion in 'Scripting' started by Landa-100, Oct 9, 2023.

  1. Landa-100

    Landa-100

    Joined:
    Sep 9, 2018
    Posts:
    17
    Code (CSharp):
    1. /// <summary>
    2.     ///  This a tradiontal style for Fire bullets 10times/s
    3.     /// </summary>
    4.     void Fire()
    5.     {
    6.         // Work well has tested.
    7.         if (isKeyPressed && Time.time >= nextFireTime)
    8.         {
    9.             nextFireTime = Time.time + fireInterval;
    10.             Instantiate(bullet, gunTransform.position, transform.rotation);
    11.         }
    12.     }
    13.     /// <summary>
    14.     /// How to implement by Awaitable Class?
    15.     /// </summary>
    16.     async void FireAsync()
    17.     {
    18.         // Unfinished code below!
    19.         await Awaitable.WaitForSecondsAsync(fireInterval);
    20.         if (isKeyPressed)
    21.             Instantiate(bullet, gunTransform.position, transform.rotation);
    22.  
    23.     }
    I thought the new Awaitable class may be able to do the same job in a more clear way. Have you tried this method before?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    Landa-100 likes this.
  3. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    291
    I have not. I've only just been learning more about async/await for a pure .NET server app I'm working on. Traditionally asynchronous operations in Unity have been handled by coroutines, which are very similar to async/await. They are both syntactic sugar that automatically compile into hidden classes that essentially create a state machine that invokes callbacks in the proper order. However, async/await doesn't have a good cancellation token for when you stop play in the editor. (Except there is a cancellation token now Unity 2022 that you can attach your own code to as a continuation. I am not sure if it's documented, but I remember seeing a post about it recently.) So, async / await has not normally been recommended in Unity because of inherent issues like your async code continuing to run even after you have stopped playing. Coroutines have issues of their own. I did see this thread about Awaitables that I have bookmarked to play around with at some point in the future, but I have not gotten around to it. The key with async operations, in my experience, comes down to cancellation. Like, in your example, what if you start up an asynchronous fire operation, but in that millisecond that character gets blown to smithereens? I imagine it would be silly for his weapon to suddenly fire a second later. Cancellation of your async ops under the appropriate conditions is the key. With coroutines you need to maintain references to the IEnumerator instances returned when you invoke the coroutine iterator methods, so you can cancel that instance later. With async / await, and I assume with awaitables, you use cancellation tokens that are passed in as arguments, and invoke cancellation at the appropriate places in your code. It seems Awaitables are at least somewhat officially promoted as a way of getting async/await style code going in your Unity project, so I don't see an inherent problem with that approach.

    https://forum.unity.com/threads/async-await-future-design.1403386/
     
  4. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    291
    Other issues that come to mind with async / await, that I have been dealing with: The whole .NET infrastructure is built under the assumption that you will have some async root method somewhere, should you need it. You can have an async Main() method now. If you use something like the .NET generic host to start a background worker service, it will have an async Task ExecuteAsync() method as your entry point. All of the advice you'll find online on the subject seems to recommend that async / await is viral, so any async method that awaits something should only really be called from and awaited in another async method, all the way down to the root of the framework (as far as you're concerned.) Whether or not people actually do that is another matter, but that seems to be the prevailing wisdom. Failure to do so is referred to as "Fire and forget," and is generally discouraged. Posts on Stack Overflow from people like Stephen Cleary, who I consider to be perhaps the pre-eminent authority on the topic you'll find online, will say things begrudgingly like, "If you MUST do fire and forget..." Everything is written from a non-Unity perspective, where there is almost certainly some pre-existing async root entry point method for your code to hang on. The assumptions about the whole architecture / framework / environment that you're working in are always an enterprise .NET application. So, finding information about how it all works, and how to initiate an async operation from a synchronous point in your code can be confusing, in my opinion. Should you use Task.Run(MyAsyncMethod()), or Task.Run(() => MyAsyncMethod()), , or Task.Run(async () => MyAsyncMethod()), or just call MyAsyncMethod(), or call MyAsyncMethod().GetAwaiter().GetResult(), and which ones will run on which threads in the thread pool? Am I starving the thread pool? Are the threads even switching? Am I on the main thread, or a background thread? Maybe others are not so confused by it all as I have been, but I find all of it a bit out of sync with the Unity dev environment (pardon the pun.) I believe some of those issues are what the Awaitable Tasks are intended to solve, but like I said I haven't played around with them yet. Looking at the Awaitable docs, the examples use an async Start() method. Maybe I'm getting old, but the syntactic sugar around asynchronous operations just leaves me feeling more confused then assisted because I still want to know exactly how everything works and what it all compiles out to. I don't like secret under the hood magic for the sake of "readability," but I do see a valid need for chaining together continuations of async operations. I just want it to be clear.
     
    Landa-100 likes this.
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,917
    I definitely would not use async code for this. Async code is primarily intended for code that needs to be executed over multiple frames without blocking the main thread.

    As noted above once you get into cancellation stuff, async code becomes pretty complicated. Not only that but it's compiler magic that generate tons more code than you're actually seeing. Completely overkill for something like firing a gun.

    I would just use a tiny timer baked into the object that needs this interval. No need to reinvent the wheel here.
     
    CodeRonnie likes this.
  6. Landa-100

    Landa-100

    Joined:
    Sep 9, 2018
    Posts:
    17
    Yes, I feel that I have to control a new thread state, and when to cancel the task, for syntactic sugar I thought it was a code template for improving production efficiency. Such as
    Code (CSharp):
    1.  public bool IsGround { get; set;}
    it automatically refactors my code under the hood.
     
  7. Landa-100

    Landa-100

    Joined:
    Sep 9, 2018
    Posts:
    17
    Yes, maybe the first solution was easy to understand.