Compare commits

...

164 Commits

Author SHA1 Message Date
dependabot[bot]
46c690e19a chore(deps): bump 1password/load-secrets-action
Bumps the dependencies group with 1 update: [1password/load-secrets-action](https://github.com/1password/load-secrets-action).


Updates `1password/load-secrets-action` from 2.0.0 to 4.0.0
- [Release notes](https://github.com/1password/load-secrets-action/releases)
- [Commits](581a835fb5...92467eb28f)

---
updated-dependencies:
- dependency-name: 1password/load-secrets-action
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 12:28:26 +00:00
Ikiru Yoshizaki
a9e27c03d4 chore: add groups for dependabot 2026-02-25 18:03:24 +09:00
Ikiru Yoshizaki
73a63b7f67 Merge pull request #680 from Cysharp/ci/nuget_release
ci: add id-token: write for NuGet Trusted Publish
2025-10-01 16:31:33 +09:00
Ikiru Yoshizaki
30bec5d5c4 ci: add id-token: write for NuGet Trusted Publish 2025-10-01 16:03:54 +09:00
Ikiru Yoshizaki
ec9204d381 Merge pull request #679 from Cysharp/feature/nuget
chore: Specify IsPackable=false on Directory.Build.props, explicitly true for target packages.
2025-09-25 01:12:10 +09:00
Ikiru Yoshizaki
9a6584ff0d chore: Specify IsPackable=false on Directory.Build.props, explicitly true for target packages. 2025-09-24 21:17:25 +09:00
Ikiru Yoshizaki
98538ef7c7 Merge pull request #677 from Cysharp/feature/docs
ci: replace docfx build from container to docfx command
2025-09-13 01:23:03 +09:00
Ikiru Yoshizaki
519590ca6e chore: exclude tests assemblies 2025-09-10 18:38:31 +09:00
Ikiru Yoshizaki
dce4366f3e ci: change docfx target from source code to Unity built assemblies 2025-09-10 18:20:17 +09:00
Ikiru Yoshizaki
88026ab14a ci: replace docfx build from container to docfx command 2025-09-10 17:20:43 +09:00
Ikiru Yoshizaki
95998ff3f2 ci: dependabot cooldown 65d2ae 2025-09-10 12:34:40 +09:00
Ikiru Yoshizaki
64285ae060 Merge pull request #672 from Cysharp/ci/nuget_readme
chore: add README.md to nuget package
2025-08-19 18:37:19 +09:00
Ikiru Yoshizaki
40e020fb02 ci: add dotnet pack and use Release 2025-08-19 17:42:54 +09:00
Ikiru Yoshizaki
e63dba1350 chore: add README.md to nuget package 2025-08-19 17:37:09 +09:00
Ikiru Yoshizaki
f213ff497e ci: missing permission 2025-05-14 15:41:23 +09:00
Ikiru Yoshizaki
7568061eda ci: fix ghalint 2025-05-14 12:39:03 +09:00
Ikiru Yoshizaki
459a572c1d chore: add .editorconfig 2025-05-14 12:38:53 +09:00
Ikiru Yoshizaki
06067cd4c8 ci: use Cysharp/Actions checkout instead of 3rd party directly 2025-03-19 15:44:16 +09:00
Ikiru Yoshizaki
d9983cfe27 Merge pull request #654 from Cysharp/feature/pin_action
ci: Pinning third party GitHub Actions sha
2025-03-18 17:33:39 +09:00
Ikiru Yoshizaki
70eb7cd3ee ci: Pinning third party GitHub Actions sha 2025-03-18 16:58:33 +09:00
Ikiru Yoshizaki
cc3c70af90 ci: update vault 2025-03-11 12:55:51 +09:00
Ikiru Yoshizaki
8042b29ff8 ci: extend timeout 2025-01-07 12:29:34 +09:00
Yoshifumi Kawai
b0d01ca75f Merge pull request #641 from hmkc/dev
Update README_CN.md
2024-12-12 16:35:32 +09:00
hmkc
7a63ab7088 Update README_CN.md
Re-translated the documentation.
2024-12-11 17:13:32 +08:00
Yoshifumi Kawai
bdf102f145 Merge pull request #639 from hmkc/dev
Update README_CN.md
2024-12-09 20:18:43 +09:00
hmkc
41cea030ab Update README_CN.md 2024-12-09 18:39:08 +08:00
Yoshifumi Kawai
f9fd769be7 Update README.md 2024-10-09 14:46:23 +09:00
neuecc
579304fe47 chore(docs): update TOC 2024-10-08 14:02:41 +00:00
Yoshifumi Kawai
740ca7ef01 Awaitable notes 2024-10-08 23:02:26 +09:00
github-actions[bot]
7c0f199fe0 feat: Update package.json to 2.5.10 2024-10-03 00:42:37 +00:00
neuecc
9a3ec31533 Merge remote-tracking branch 'origin/master' 2024-10-03 09:41:47 +09:00
neuecc
37d8f4f48e C# 8 #624 2024-10-03 09:41:30 +09:00
github-actions[bot]
27a0c06ede feat: Update package.json to 2.5.9 2024-10-01 06:16:52 +00:00
neuecc
e4082ecd75 Merge remote-tracking branch 'origin/master' 2024-10-01 14:59:02 +09:00
neuecc
3b0fd784ff meta 2024-10-01 14:58:57 +09:00
neuecc
dc9ebfd765 Add AsyncInstantiateOperation.WithCancellation, ToUniTask for Unity 2022.3.20/Unity 2022.3 or newer 2024-10-01 14:50:13 +09:00
Yoshifumi Kawai
005c83fbd7 Merge pull request #623 from Cysharp/feature/ci
ci: remove Unity 2021 LTS from Build matrix
2024-10-01 10:44:30 +09:00
github-actions[bot]
5984b67ecb feat: Update package.json to 2.5.8 2024-09-30 11:45:57 +00:00
neuecc
05fdf48058 Merge remote-tracking branch 'origin/master' 2024-09-30 20:43:06 +09:00
Yoshifumi Kawai
647ed6ff82 Merge pull request #621 from dvsilch/master
add generic type UnityAction support & fix typo
2024-09-30 20:42:57 +09:00
neuecc
0826b7e976 Add UniTask.WhenEach 2024-09-30 20:42:17 +09:00
dvsilch
a51632cd4b fix: add overloads for CancellationToken 2024-09-30 10:13:29 +08:00
dvsilch
bf945a7ef4 fix: rename parameters and type parameters 2024-09-30 10:11:36 +08:00
dvsilch
353f15e94f fix: add .Forget() call and rename paramters to keep coding style consistent 2024-09-27 23:45:05 +08:00
dvsilch
cf19f18662 fix: typo 2024-09-27 22:57:36 +08:00
dvsilch
fdb9d1cf95 feature: add overload in UniTask.UnityAction to support generic type UnityAction 2024-09-27 22:57:05 +08:00
Yoshifumi Kawai
2e0917428b Merge pull request #620 from albermotion/master
Added a note for users using Unity 2023.1 or newer to prevent compilation error
2024-09-27 09:48:04 +09:00
Alberto
0b16005f4b Added a note for users using Unity 2023.1 or newer 2024-09-26 22:33:37 +02:00
github-actions[bot]
74bbe87b58 feat: Update package.json to 2.5.7 2024-09-26 06:24:48 +00:00
Yoshifumi Kawai
6f4131539b Merge pull request #619 from kochounoyume/add-state-argument
Add overload in UniTask.WaitUntil, UniTask.WaitWhile and UniTask.Defer
2024-09-26 12:52:56 +09:00
Kochoyume
06283f0ffb Add to the code that I forgot to write 2024-09-24 23:58:28 +09:00
Kochoyume
dfe5ee43c2 Add overload in UniTask.WaitUntil, UniTask.WaitWhile and UniTask.Defer to avoid closure allocation 2024-09-24 23:37:44 +09:00
Ikiru Yoshizaki
eaa553dc83 ci: remove Unity 2021 LTS from Build matrix 2024-09-24 18:30:54 +09:00
github-actions[bot]
83d8a2b424 feat: Update package.json to 2.5.6 2024-09-24 09:20:07 +00:00
Yoshifumi Kawai
4d204e4aa6 Update build-debug.yml 2024-09-24 17:49:34 +09:00
Yoshifumi Kawai
87e164e275 Merge pull request #600 from Iblis/textmeshpro_unity6_adoptions
TextMeshPro Unity6 adoptions
2024-09-24 17:16:20 +09:00
Yoshifumi Kawai
b63eb8d090 Merge pull request #610 from LucianoPC/forget-ignored-task
Forget ignored task
2024-09-24 17:14:40 +09:00
neuecc
75119acb50 Fix WaitWhile(bool cancelImmediately = true) does not work correctly
, and when cancelImmediately = true and canceled task remove Trakcking immediately
2024-09-24 17:12:50 +09:00
Ikiru Yoshizaki
f7b3c2fbe1 ci: update release unity version to 2022.3.39f1 [skip ci] 2024-09-18 17:06:00 +09:00
Ikiru Yoshizaki
b317ecfa01 ci: set max-parallel to restrict up to 2 run [skip ci] 2024-09-18 16:27:42 +09:00
Ikiru Yoshizaki
7b05569ef7 ci: single job 2024-09-18 16:27:42 +09:00
Luciano Prestes Cavalcanti
e0465c6c2c Forget ignored task 2024-08-19 08:18:57 -03:00
Yoshifumi Kawai
9057452c86 Merge pull request #607 from Redpenguine/master
fix: incorrect download progress reporting
2024-08-14 19:54:15 +09:00
Mykyta Myronenko
a2f6f84bde fix: incorrect download progress reporting 2024-08-10 19:29:54 +03:00
Ikiru Yoshizaki
f057abff0f chore: remove Unity UserSettigs from git handling 2024-07-31 16:11:03 +09:00
Ikiru Yoshizaki
c61a7d9961 Merge pull request #605 from Cysharp/dependabot/github_actions/peaceiris/actions-gh-pages-4
chore(deps): bump peaceiris/actions-gh-pages from 3 to 4
2024-07-30 17:09:56 +09:00
Ikiru Yoshizaki
9587f2eeec Merge pull request #606 from Cysharp/dependabot/github_actions/technote-space/toc-generator-4.3.1
chore(deps): bump technote-space/toc-generator from 2.4.0 to 4.3.1
2024-07-30 17:09:49 +09:00
Ikiru Yoshizaki
550784f31c ci: check not dependabot 2024-07-30 17:07:43 +09:00
Ikiru Yoshizaki
11b3282b3d ci: Don't allow dependabot to run Unity CI 2024-07-30 17:01:09 +09:00
dependabot[bot]
b2532b0798 chore(deps): bump technote-space/toc-generator from 2.4.0 to 4.3.1
Bumps [technote-space/toc-generator](https://github.com/technote-space/toc-generator) from 2.4.0 to 4.3.1.
- [Release notes](https://github.com/technote-space/toc-generator/releases)
- [Changelog](https://github.com/technote-space/toc-generator/blob/main/.releasegarc)
- [Commits](https://github.com/technote-space/toc-generator/compare/v2.4.0...v4.3.1)

---
updated-dependencies:
- dependency-name: technote-space/toc-generator
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 06:42:27 +00:00
dependabot[bot]
4fc41ecb17 chore(deps): bump peaceiris/actions-gh-pages from 3 to 4
Bumps [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) from 3 to 4.
- [Release notes](https://github.com/peaceiris/actions-gh-pages/releases)
- [Changelog](https://github.com/peaceiris/actions-gh-pages/blob/main/CHANGELOG.md)
- [Commits](https://github.com/peaceiris/actions-gh-pages/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peaceiris/actions-gh-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 06:42:24 +00:00
Ikiru Yoshizaki
e52663cef6 Merge pull request #570 from Cysharp/ci/dependabot
ci: add depndabot.yaml to update github-actions
2024-07-30 15:41:57 +09:00
Ikiru Yoshizaki
1827be2de7 Merge pull request #603 from Cysharp/feature/test
ci: Change Unity UnitTest from Mono to IL2CPP and enabled on CI
2024-07-30 15:41:47 +09:00
Ikiru Yoshizaki
8560561ef3 chore: remove Layouts for Unity2022 2024-07-30 15:17:05 +09:00
Ikiru Yoshizaki
2019f1fa7f ci: update CI Unity 2024-07-30 15:16:54 +09:00
Ikiru Yoshizaki
9e2265d148 ci: split job for build and test 2024-07-30 14:46:59 +09:00
Ikiru Yoshizaki
9d02279822 ci: extend Unity Build timeout to 20m 2024-07-30 14:25:31 +09:00
Ikiru Yoshizaki
17ce06d93c chore: remove com.unity.ai.navigation as 2021.3.39f1 failed and not inuse 2024-07-30 14:07:59 +09:00
Ikiru Yoshizaki
7b810413fe fix: missing include Packages folder for Unity 2024-07-30 14:00:06 +09:00
Ikiru Yoshizaki
d78c0d6c02 ci: remove garbage folder 2024-07-30 12:36:34 +09:00
Ikiru Yoshizaki
d248acc7d1 chore: bump RuntimeUnitTestToolkit to 2.6.0 to adapt Unity 2022 Build API change 2024-07-30 12:34:46 +09:00
Ikiru Yoshizaki
4c0b1f753a chore: bump UniTask unity version to 2022.3.39f1 2024-07-24 20:11:26 +09:00
Ikiru Yoshizaki
7bb6feda55 chore: switch to src/UniTask/*.cproj 2024-07-24 20:10:17 +09:00
Ikiru Yoshizaki
f8a501290a chore: upload 2024-07-24 20:08:11 +09:00
Ikiru Yoshizaki
cb497c9eb5 ci: bump Unity version to LTS, Unity Unit Test with LTS. 2024-07-24 19:35:31 +09:00
Ikiru Yoshizaki
342a37a074 ci: add strategy.fail-fast: false 2024-07-24 17:44:49 +09:00
Ikiru Yoshizaki
c3146ec74f ci: change Unit Test to IL2CPP 2024-07-24 17:24:01 +09:00
Ikiru Yoshizaki
df16813fae ci: bump actions/checkout@v3 -> v4 2024-07-24 17:22:28 +09:00
Philipp Walser
8d98bbc7ba added version contraint, tmp is part of ugui since v2.0.0 2024-07-12 19:05:38 +02:00
Philipp Walser
bb095697f7 added versionDefine to set TMP support if ugui is found, too 2024-07-12 19:01:59 +02:00
github-actions[bot]
cdf88c6a6a feat: Update package.json to 2.5.5 2024-07-01 06:47:55 +00:00
Yoshifumi Kawai
4eee2c9270 Merge pull request #599 from xfvvvv/patch-1
Fix onTextSelection and onEndTextSelection stack overflow issue
2024-07-01 15:45:30 +09:00
Chelal
222b1401f5 Fix onTextSelection and onEndTextSelection stack overflow issue 2024-06-29 21:51:27 +08:00
Ikiru Yoshizaki
fbe0bf8515 Merge pull request #579 from Cysharp/feature/artifact
ci: change upload-artifact & download-artifact to Cysharp/Actions
2024-04-30 14:36:53 +09:00
Ikiru Yoshizaki
626685292c ci: set upload-artifact retention-period: 1 2024-04-30 14:06:49 +09:00
Ikiru Yoshizaki
67794f5cdd ci: change upload-artifact & download-artifact to Cysharp/Actions 2024-04-30 12:43:17 +09:00
Mayuki Sawatari
2e225fb841 Merge pull request #571 from Cysharp/feature/secret
feat: change load secret from op
2024-04-17 18:29:49 +09:00
Ikiru Yoshizaki
935523f25c feat: change load secret from op 2024-04-17 16:09:55 +09:00
Ikiru Yoshizaki
3b3f7ebd3e ci: add depndabot.yaml to update github-actions 2024-04-16 12:14:52 +09:00
Yoshifumi Kawai
88ecfa2992 Merge pull request #565 from divdeploy/master
chore: fix comment
2024-04-08 17:03:08 +09:00
divdeploy
aa70fe5c5a chore: fix comment
Signed-off-by: divdeploy <chenguangxue@outlook.com>
2024-04-05 23:28:56 +08:00
Yoshifumi Kawai
0c8057c668 Merge pull request #538 from kochounoyume/argument-allowed-factory
Factory method allowed argument
2024-04-05 13:30:39 +09:00
Kochoyume
28867646b0 Fix T state as the first argument 2024-03-31 00:05:18 +09:00
Kochoyume
ca60864021 Fix comment 2024-03-30 23:57:43 +09:00
Kochoyume
1e4561da22 Fix comment 2024-03-30 23:57:43 +09:00
Kochoyume
1496cc990a Fix misspelling 2024-03-30 23:57:43 +09:00
Kochoyume
7bb3bf8d0e Update UniTask.Create, Unitask.Action and UniTask.UnityAction 2024-03-30 23:57:43 +09:00
github-actions[bot]
d057074f17 feat: Update package.json to 2.5.4 2024-03-28 06:22:15 +00:00
hadashiA
b724a2aa84 Merge pull request #557 from Cysharp/hadashiA/fix-cancel-immediate
Fix unintended returning to pool with cancelImmediately
2024-03-28 15:21:40 +09:00
hadashiA
938ddd5356 Tweaks 2024-03-28 15:15:55 +09:00
hadashiA
911c37d4d8 Tweaks 2024-03-28 15:14:10 +09:00
hadashiA
caccccb0b5 Fix timing to returning to pool 2024-03-28 15:00:41 +09:00
hadashiA
a48f11d31b Fix unintended returning to pool with cancelImmediately (WaitUntil,etc) 2024-03-28 14:46:37 +09:00
hadashiA
fcd93feb56 Fix unintended returning to pool with cancelImmediately (AsyncOperation) 2024-03-28 14:45:55 +09:00
hadashiA
b472b23773 Fix unintended returning to pool with cancelImmediately (UniTask.Delay) 2024-03-28 14:29:30 +09:00
Yoshifumi Kawai
01c8fada1f Merge pull request #542 from Cysharp/feature/new_release
ci: Cysharp/Actions/.github/workflows/create-release.yaml
2024-03-25 18:30:50 +09:00
Ikiru Yoshizaki
10cd137126 ci: Cysharp/Actions/.github/workflows/create-release.yaml 2024-02-07 21:04:48 +09:00
Yoshifumi Kawai
809d23edae Merge pull request #541 from TORISOUP/fix_ToArray
Optimization of ToArray in Merge method
2024-02-04 23:19:47 +09:00
TORISOUP
d38731bc44 fix call ToArray 2024-02-04 20:51:21 +09:00
github-actions[bot]
64792b672d feat: Update package.json to 2.5.3 2024-01-25 12:52:16 +00:00
neuecc
3892cc2299 Merge branch 'master' of https://github.com/Cysharp/UniTask 2024-01-25 21:51:45 +09:00
neuecc
5bfccaa3b6 autoReleaseWhenCancelled -> autoReleaseWhenCanceled 2024-01-25 21:51:40 +09:00
github-actions[bot]
b49b7332bb feat: Update package.json to 2.5.2 2024-01-25 11:43:24 +00:00
neuecc
08184af737 simplify UNITASK define 2024-01-25 20:42:42 +09:00
Yoshifumi Kawai
0ef6c59385 Merge pull request #535 from kochounoyume/valuetask-error-netframework
UniTask does not work in .NET Framework environment after adding UniTask.AsValueTask
2024-01-25 20:26:21 +09:00
Yoshifumi Kawai
4b0bd3b509 Merge pull request #536 from Cysharp/hadashiA/release-handle
Fix "Release handle when cancellation is requested"
2024-01-25 20:26:13 +09:00
Kochoyume
9a4720d180 Fix:Addressed generic parameter attributes error in the .Net Framework 2024-01-25 20:05:45 +09:00
hadashiA
b99646558c Add a flag autoReleaseWhenCancelled to addressable extensionhs 2024-01-25 19:03:00 +09:00
hadashiA
ee12dd9ae7 Fix omissions in Addressable.Release and IsValid checks 2024-01-25 17:47:06 +09:00
Kochoyume
006e0f2c81 Revert "Fix conditional compilation to work with .NET Framework"
This reverts commit 7d31299b5c.
2024-01-25 16:16:01 +09:00
Kochoyume
7d31299b5c Fix conditional compilation to work with .NET Framework 2024-01-25 01:02:57 +09:00
Yoshifumi Kawai
fe8bf834e6 Update README.md 2024-01-24 10:47:31 +09:00
github-actions[bot]
5843258e8c feat: Update package.json to 2.5.1 2024-01-24 01:44:21 +00:00
Yoshifumi Kawai
6cd002645e Merge pull request #533 from doyasu24/feature/add-unitask-asvaluetask-for-unity
add UniTask.AsValueTask for Unity
2024-01-24 10:41:39 +09:00
doyasu24
4fe0861714 fix: use UNITY_2018_3_OR_NEWER instead of UNITY_2018_1_OR_NEWER 2024-01-19 23:42:17 +09:00
doyasu24
fbbba061dd ValueTask is available in Unity 2021_2_OR_NEWER or .NET Core 2024-01-19 23:40:28 +09:00
doyasu24
81f2e37ea5 add UniTask.AsValueTask for Unity 2024-01-19 00:29:21 +09:00
Yoshifumi Kawai
66de0d3a58 Merge pull request #519 from Cysharp/guitarrapc-patch-1
Use Cysharp/Actions/setup-dotnet default version
2024-01-12 15:34:54 +09:00
Yoshifumi Kawai
beb10abbf7 Merge pull request #524 from Saismirk/master
Fixed typo in TimeoutWithoutException summary.
2024-01-12 15:22:35 +09:00
Saismirk
d60f64761b Fixed typo in TimeoutWithoutException summary. 2023-12-10 12:53:52 -05:00
Luciano Prestes Cavalcanti
36ac0863ad Release handle when cancellation is requested 2023-11-22 08:09:03 -03:00
Ikiru Yoshizaki
104f8e09ca Update build-release.yml 2023-11-15 12:09:34 +09:00
Ikiru Yoshizaki
cfbff008c4 Use Cysharp/Actions/setup-dotnet default version
## tl;dr;

It support both .NET 6,7 and 8. No need specify version.
2023-11-15 11:55:27 +09:00
Yoshifumi Kawai
5cc97c7f00 ReadMe 2023-11-02 18:23:43 +09:00
github-actions[bot]
5666292496 feat: Update package.json to 2.5.0 2023-11-02 05:03:47 +00:00
hadashiA
1288cbc128 Merge pull request #518 from Cysharp/hadashiA/awaitable-to-unitask
Add Awaitable.AsUniTask()
2023-11-02 14:02:36 +09:00
hadashiA
5f3aa18f38 Add Awaitable<T>.ToUniTask 2023-11-02 13:54:55 +09:00
hadashiA
0970ae8c31 Merge pull request #517 from Cysharp/hadashiA/cancel-immediately-flag
Add a flag to cancel immediately instead of player loop
2023-11-02 13:52:12 +09:00
hadashiA
ad23f7fb29 Fix #if directive that should be in .tt 2023-11-02 12:54:59 +09:00
hadashiA
a4be8f316e Add Awaitable.AsUniTask() 2023-11-02 12:35:05 +09:00
hadashiA
370425578f Fix AssetBundleRequestAll 2023-11-02 12:24:41 +09:00
hadashiA
1b76f77608 Add test 2023-11-02 12:24:41 +09:00
hadashiA
0579984355 Use AsyncOperation.completed callback usually 2023-11-02 12:24:41 +09:00
hadashiA
39cf81d2ab Add test for unity AsyncOperation 2023-11-02 12:24:41 +09:00
hadashiA
0a203c8db9 Cache Action in advance 2023-11-02 12:24:41 +09:00
hadashiA
55be4dba82 Add callback handler to AsyncOperationConfiguredSource instead to create other sources 2023-11-02 12:24:41 +09:00
hadashiA
f0adf36633 Use AsyncOperation.completed handler with new optional argument 2023-11-02 12:24:41 +09:00
hadashiA
24afc4f3eb Add cancelImmediately flag for assetbundle extensions 2023-11-02 12:24:41 +09:00
hadashiA
2cf06af433 Change AsyncEnumerable factory to use OperationCanceledException 2023-11-02 12:24:36 +09:00
hadashiA
94be2e748b Add cancelImmediately flag for addressable extensions 2023-10-27 15:06:12 +09:00
hadashiA
7f582e5e29 Update README 2023-10-27 14:52:58 +09:00
hadashiA
3f042c8886 Add cancel immediate flag 2023-10-27 12:42:12 +09:00
Yoshifumi Kawai
6f5d818544 Merge pull request #515 from ananttheant/fix/readme-typo
Fix typo in README.md
2023-10-26 13:11:42 +09:00
Anant Sharma
2ccb37cb02 Fix typo in README.md 2023-10-25 15:24:27 +01:00
83 changed files with 4259 additions and 2738 deletions

13
.config/dotnet-tools.json Normal file
View File

@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"docfx": {
"version": "2.78.3",
"commands": [
"docfx"
],
"rollForward": false
}
}
}

41
.editorconfig Normal file
View File

@@ -0,0 +1,41 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
# Visual Studio Spell checker configs (https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker?view=vs-2022#how-to-customize-the-spell-checker)
spelling_exclusion_path = ./exclusion.dic
[*.cs]
indent_size = 4
charset = utf-8-bom
end_of_line = unset
# Solution files
[*.{sln,slnx}]
end_of_line = unset
# MSBuild project files
[*.{csproj,props,targets}]
end_of_line = unset
# Xml config files
[*.{ruleset,config,nuspec,resx,runsettings,DotSettings}]
end_of_line = unset
[*{_AssemblyInfo.cs,.notsupported.cs}]
generated_code = true
# C# code style settings
[*.{cs}]
dotnet_diagnostic.IDE0044.severity = none # IDE0044: Make field readonly
# https://stackoverflow.com/questions/79195382/how-to-disable-fading-unused-methods-in-visual-studio-2022-17-12-0
dotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private member
dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure

18
.github/dependabot.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
# ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly" # Check for updates to GitHub Actions every week
groups:
dependencies:
patterns:
- "*"
cooldown:
default-days: 14 # Wait 14 days before creating another PR for the same dependency. This will prevent vulnerability on the package impact.
ignore:
# I just want update action when major/minor version is updated. patch updates are too noisy.
- dependency-name: "*"
update-types:
- version-update:semver-patch

93
.github/workflows/build-debug.yaml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: Build-Debug
on:
push:
branches:
- "master"
pull_request:
branches:
- "master"
jobs:
build-dotnet:
permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: Cysharp/Actions/.github/actions/checkout@main
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
- run: dotnet build -c Release
- run: dotnet test -c Release
- run: dotnet pack -c Release --no-build -p:IncludeSymbols=true -o $GITHUB_WORKSPACE/artifacts
build-unity:
if: ${{ ((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')) && github.triggering_actor != 'dependabot[bot]' }}
strategy:
fail-fast: false
max-parallel: 2
matrix:
unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS
permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 30 # Unity build takes more than 20min.
steps:
- name: Load secrets
id: op-load-secret
uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 # v4.0.0
with:
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- uses: Cysharp/Actions/.github/actions/checkout@main
# Execute scripts: Export Package
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
- name: Build Unity (.unitypacakge)
if: ${{ startsWith(matrix.unity, '2022') }} # only execute once
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
# Execute UnitTest
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64
- name: Build UnitTest (IL2CPP)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: UnitTestBuilder.BuildUnitTest
customParameters: "/headless /ScriptBackend IL2CPP"
- name: Check UnitTest file is generated
run: ls -lR ./src/UniTask/bin/UnitTest
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Store artifacts.
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
with:
name: UniTask.unitypackage-${{ matrix.unity }}.zip
path: ./src/UniTask/*.unitypackage
retention-days: 1

View File

@@ -1,73 +0,0 @@
name: Build-Debug
on:
push:
branches:
- "master"
pull_request:
branches:
- "master"
jobs:
build-dotnet:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with:
dotnet-version: |
6.0.x
7.0.x
- run: dotnet build -c Debug
- run: dotnet test -c Debug
build-unity:
if: "((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:'))"
strategy:
matrix:
unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
# Execute scripts: RuntimeUnitTestToolkit
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
- name: Build UnitTest(Linux64, mono)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: UnitTestBuilder.BuildUnitTest
customParameters: /headless /ScriptBackend mono
- name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
# Execute scripts: Export Package
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
- name: Build Unity (.unitypacakge)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Store artifacts.
- uses: actions/upload-artifact@v2
with:
name: UniTask.unitypackage-${{ matrix.unity }}.zip
path: ./src/UniTask/*.unitypackage

58
.github/workflows/build-docs.yaml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: build-docs
on:
push:
branches:
- master
- feature/docs
jobs:
run-docfx:
if: ${{ ((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')) && github.triggering_actor != 'dependabot[bot]' }}
permissions:
contents: write
pages: write
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Load secrets
id: op-load-secret
uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 # v4.0.0
with:
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- uses: Cysharp/Actions/.github/actions/checkout@main
# Execute scripts: Export Package
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
- name: Build Unity (.unitypackage)
uses: Cysharp/Actions/.github/actions/unity-builder@main
env:
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with:
projectPath: src/UniTask
unityVersion: "2022.3.39f1"
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
- uses: Cysharp/Actions/.github/actions/checkout@main
with:
repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
- name: dotnet tool restore
run: dotnet tool restore
- name: Docfx metadata
run: dotnet docfx metadata docs/docfx.json
- name: Docfx build
run: dotnet docfx build docs/docfx.json
- name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site

View File

@@ -1,31 +0,0 @@
name: build-docs
on:
push:
branches:
- master
- feature/docs
jobs:
run-docfx:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
with:
repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate
- uses: Kirbyrawr/docfx-action@master
name: Docfx metadata
with:
args: metadata docs/docfx.json
- uses: Kirbyrawr/docfx-action@master
name: Docfx build
with:
args: build docs/docfx.json
- name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site

View File

@@ -12,52 +12,63 @@ on:
default: false default: false
type: boolean type: boolean
env:
GIT_TAG: ${{ github.event.inputs.tag }}
DRY_RUN: ${{ github.event.inputs.dry-run }}
jobs: jobs:
update-packagejson: update-packagejson:
permissions:
actions: read
contents: write
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
with: with:
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
tag: ${{ github.event.inputs.tag }} tag: ${{ inputs.tag }}
dry-run: ${{ fromJson(github.event.inputs.dry-run) }} dry-run: ${{ inputs.dry-run }}
build-dotnet: build-dotnet:
needs: [update-packagejson] needs: [update-packagejson]
runs-on: ubuntu-latest permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with:
dotnet-version: |
3.1.x
6.0.x
# build and pack # build and pack
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }} - run: dotnet build -c Release -p:Version=${{ inputs.tag }}
- run: dotnet test -c Release --no-build - run: dotnet test -c Release --no-build
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} -o ./publish - run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: Cysharp/Actions/.github/actions/upload-artifact@main
with: with:
name: nuget name: nuget
path: ./publish/ path: ./publish/
retention-days: 1
build-unity: build-unity:
needs: [update-packagejson] needs: [update-packagejson]
strategy: strategy:
matrix: matrix:
unity: ["2019.3.9f1"] unity: ["2022.3.39f1"]
runs-on: ubuntu-latest permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Load secrets
id: op-load-secret
uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 # v4.0.0
with:
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
# Execute scripts: Export Package # Execute scripts: Export Package
@@ -65,9 +76,9 @@ jobs:
- name: Build Unity (.unitypacakge) - name: Build Unity (.unitypacakge)
uses: Cysharp/Actions/.github/actions/unity-builder@main uses: Cysharp/Actions/.github/actions/unity-builder@main
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
with: with:
projectPath: src/UniTask projectPath: src/UniTask
unityVersion: ${{ matrix.unity }} unityVersion: ${{ matrix.unity }}
@@ -79,46 +90,33 @@ jobs:
directory: src/UniTask directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: Cysharp/Actions/.github/actions/upload-artifact@main
with: with:
name: UniTask.${{ env.GIT_TAG }}.unitypackage name: UniTask.${{ inputs.tag }}.unitypackage
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage path: ./src/UniTask/UniTask.${{ inputs.tag }}.unitypackage
retention-days: 1
# release
create-release: create-release:
if: github.event.inputs.dry-run == 'false'
needs: [update-packagejson, build-dotnet, build-unity] needs: [update-packagejson, build-dotnet, build-unity]
runs-on: ubuntu-latest permissions:
timeout-minutes: 10 contents: write
steps: id-token: write # required for NuGet Trusted Publish
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
# Create Releases
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ env.GIT_TAG }} commit-id: ${{ needs.update-packagejson.outputs.sha }}
release_name: Ver.${{ env.GIT_TAG }} dry-run: ${{ inputs.dry-run }}
commitish: ${{ needs.update-packagejson.outputs.sha }} tag: ${{ inputs.tag }}
draft: true nuget-push: true
prerelease: false release-upload: true
# Download(All) Artifacts to current directory release-asset-path: ./UniTask.${{ inputs.tag }}.unitypackage/UniTask.${{ inputs.tag }}.unitypackage
- uses: actions/download-artifact@v2 secrets: inherit
# Upload to NuGet
- run: dotnet nuget push "./nuget/*.nupkg" --skip-duplicate -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
# Upload to Releases(unitypackage)
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
asset_content_type: application/octet-stream
cleanup: cleanup:
if: needs.update-packagejson.outputs.is-branch-created == 'true' if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
needs: [update-packagejson, build-dotnet, build-unity] needs: [update-packagejson, build-dotnet, build-unity]
permissions:
contents: write
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
with: with:
branch: ${{ needs.update-packagejson.outputs.branch-name }} branch: ${{ needs.update-packagejson.outputs.branch-name }}

View File

@@ -7,4 +7,6 @@ on:
jobs: jobs:
detect: detect:
permissions:
contents: read
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main

View File

@@ -7,4 +7,8 @@ on:
jobs: jobs:
stale: stale:
permissions:
contents: read
pull-requests: write
issues: write
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main

15
.github/workflows/toc.yaml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: TOC Generator
on:
push:
paths:
- 'README.md'
jobs:
toc:
permissions:
contents: write
uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main
with:
TOC_TITLE: "## Table of Contents"
secrets: inherit

View File

@@ -1,15 +0,0 @@
name: TOC Generator
on:
push:
paths:
- 'README.md'
jobs:
generateTOC:
name: TOC Generator
runs-on: ubuntu-latest
steps:
- uses: technote-space/toc-generator@v2.4.0
with:
TOC_TITLE: "## Table of Contents"

157
.gitignore vendored
View File

@@ -100,7 +100,19 @@ publish
*.Publish.xml *.Publish.xml
# NuGet Packages Directory # NuGet Packages Directory
packages *.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
# packages # upm pacakge will use Packages
# **/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
# !**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Windows Azure Build Output # Windows Azure Build Output
csx csx
@@ -130,134 +142,15 @@ UpgradeLog*.XML
Assets/WSATestCertificate.pfx Assets/WSATestCertificate.pfx
.vs/ .vs/
Assembly-CSharp\.csproj # Unity
UniRx\.Async\.csproj # Unity
.vsconfig
UniRx\.Async\.Editor\.csproj src/UniTask/Library/*
src/UniTask/Temp/*
UniRx\.Async\.Tests\.csproj src/UniTask/Logs/*
src/UniTask/[Uu]ser[Ss]ettings/
UniTask\.sln src/UniTask/*.sln
src/UniTask/*.csproj
RuntimeUnitTestToolkit\.csproj src/UniTask/*.unitypackage
!src/UniTask/Packages/
Assembly-CSharp-Editor\.csproj
UniRx\.Async\.unitypackage
UniRx.Async.Tests.Editor.csproj
src/UniTask/UniTask.csproj
src/UniTask/UniTask.Editor.csproj
src/UniTask/UniTask.Tests.csproj
src/UniTask/UniTask.Tests.Editor.csproj
src/UniTask/UniTask.*.unitypackage
src/UniTask/UniTask.Linq.csproj
src/UniTask/DOTween.Modules.csproj
src/UniTask/Unity.Addressables.csproj
src/UniTask/Unity.Addressables.Editor.csproj
src/UniTask/Unity.Analytics.DataPrivacy.csproj
src/UniTask/Unity.Recorder.csproj
src/UniTask/Unity.Recorder.Editor.csproj
src/UniTask/Unity.ResourceManager.csproj
src/UniTask/Unity.Rider.Editor.csproj
src/UniTask/Unity.ScriptableBuildPipeline.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
src/UniTask/Unity.TextMeshPro.csproj
src/UniTask/Unity.TextMeshPro.Editor.csproj
src/UniTask/Unity.Timeline.csproj
src/UniTask/Unity.Timeline.Editor.csproj
src/UniTask/Unity.VisualStudio.Editor.csproj
src/UniTask/Unity.VSCode.Editor.csproj
src/UniTask/UnityEditor.CacheServer.csproj
src/UniTask/UnityEditor.TestRunner.csproj
src/UniTask/UnityEditor.UI.csproj
src/UniTask/UnityEngine.Advertisements.csproj
src/UniTask/UnityEngine.Monetization.csproj
src/UniTask/UnityEngine.TestRunner.csproj
src/UniTask/UnityEngine.UI.csproj
src/UniTask/TempAsm.csproj
src/UniTask/UniTask.Addressables.csproj
src/UniTask/UniTask.DOTween.csproj
src/UniTask/UniTask.TextMeshPro.csproj
src/UniTask/RuntimeUnitTestToolkit.Player.csproj
src/UniTask/TempAsm.Player.csproj
src/UniTask/UniTask.Addressables.Player.csproj
src/UniTask/UniTask.DOTween.Player.csproj
src/UniTask/UniTask.Linq.Player.csproj
src/UniTask/UniTask.Player.csproj
src/UniTask/UniTask.Tests.Player.csproj
src/UniTask/UniTask.TextMeshPro.Player.csproj
src/UniTask/Unity.Addressables.Player.csproj
src/UniTask/Unity.Analytics.DataPrivacy.Player.csproj
src/UniTask/Unity.ResourceManager.Player.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Player.csproj
src/UniTask/Unity.TextMeshPro.Player.csproj
src/UniTask/Unity.Timeline.Player.csproj
src/UniTask/UnityEngine.Advertisements.Player.csproj
src/UniTask/UnityEngine.Monetization.Player.csproj
src/UniTask/UnityEngine.TestRunner.Player.csproj
src/UniTask/UnityEngine.UI.Player.csproj
src/UniTask/DOTween.Modules.Player.csproj
src/UniTask/Assembly-CSharp.Player.csproj
src/UniTask/Unity.EditorCoroutines.Editor.csproj
src/UniTask/.vsconfig
src/UniTask/Logs/ApiUpdaterCheck.txt
src/UniTask/Assembly-CSharp-firstpass.csproj

27
Directory.Build.props Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)opensource.snk</AssemblyOriginatorKeyFile>
<!-- NuGet Package Information -->
<IsPackable>false</IsPackable>
<PackageVersion>$(Version)</PackageVersion>
<Company>Cysharp</Company>
<Authors>Cysharp</Authors>
<Copyright>© Cysharp, Inc.</Copyright>
<PackageTags>task;async</PackageTags>
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Icon.png" Pack="true" PackagePath="\" />
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)LICENSE" />
</ItemGroup>
</Project>

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -33,6 +33,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq) - [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
- [Awaitable Events](#awaitable-events) - [Awaitable Events](#awaitable-events)
- [Channel](#channel) - [Channel](#channel)
- [vs Awaitable](#vs-awaitable)
- [For Unit Testing](#for-unit-testing) - [For Unit Testing](#for-unit-testing)
- [ThreadPool limitation](#threadpool-limitation) - [ThreadPool limitation](#threadpool-limitation)
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation) - [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
@@ -51,7 +52,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
Getting started Getting started
--- ---
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page. Install via [UPM package](#upm-package) with git reference or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases).
```csharp ```csharp
// extension awaiter/methods can be used by this namespace // extension awaiter/methods can be used by this namespace
@@ -67,6 +68,7 @@ async UniTask<string> DemoAsync()
await SceneManager.LoadSceneAsync("scene2"); await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T> // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
@@ -160,7 +162,7 @@ UniTask provides three pattern of extension methods.
> Note: AssetBundleRequest has `asset` and `allAssets`, default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method. > Note: AssetBundleRequest has `asset` and `allAssets`, default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
The type of `UniTask` can use utilities like `UniTask.WhenAll`, `UniTask.WhenAny`. They are like `Task.WhenAll`/`Task.WhenAny` but the return type is more useful. They return value tuples so you can deconstruct each result and pass multiple types. The type of `UniTask` can use utilities like `UniTask.WhenAll`, `UniTask.WhenAny`, `UniTask.WhenEach`. They are like `Task.WhenAll`/`Task.WhenAny` but the return type is more useful. They return value tuples so you can deconstruct each result and pass multiple types.
```csharp ```csharp
public async UniTaskVoid LoadManyAsync() public async UniTaskVoid LoadManyAsync()
@@ -293,6 +295,8 @@ public class MyBehaviour : MonoBehaviour
} }
``` ```
After Unity 2022.2, Unity adds CancellationToken in [MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html) and [Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html).
When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.` When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.`
And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`. And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`.
@@ -336,6 +340,18 @@ if (isCanceled)
Note: Only suppress throws if you call directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not suppress throws. Note: Only suppress throws if you call directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not suppress throws.
Some features that use Unity's player loop, such as `UniTask.Yield` and `UniTask.Delay` etc, determines CancellationToken state on the player loop.
This means it does not cancel immediately upon `CancellationToken` fired.
If you want to change this behaviour, the cancellation to be immediate, set the `cancelImmediately` flag as an argument.
```csharp
await UniTask.Yield(cancellationToken, cancelImmediately: true);
```
Note: Setting `cancelImmediately` to true and detecting an immediate cancellation is more costly than the default behavior.
This is because it uses `CancellationToken.Register`; it is heavier than checking CancellationToken on the player loop.
Timeout handling Timeout handling
--- ---
Timeout is a variation of cancellation. You can set timeout by `CancellationTokenSouce.CancelAfterSlim(TimeSpan)` and pass CancellationToken to async methods. Timeout is a variation of cancellation. You can set timeout by `CancellationTokenSouce.CancelAfterSlim(TimeSpan)` and pass CancellationToken to async methods.
@@ -514,6 +530,9 @@ It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.
In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`. In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
> Note: When using Unity 2023.1 or newer, ensure you have `using UnityEngine;` in the using statements of your file when working with new `UnityEngine.Awaitable` methods like `SceneManager.LoadSceneAsync`.
> This prevents compilation errors by avoiding the use of the `UnityEngine.AsyncOperation` version.
In the stacktrace, you can check where it is running in playerloop. In the stacktrace, you can check where it is running in playerloop.
![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png) ![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png)
@@ -688,7 +707,7 @@ Unity 2020.2 supports C# 8.0 so you can use `await foreach`. This is the new Upd
```csharp ```csharp
// Unity 2020.2, C# 8.0 // Unity 2020.2, C# 8.0
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token)) await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate().WithCancellation(token))
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
} }
@@ -698,10 +717,23 @@ In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost
```csharp ```csharp
// C# 7.3(Unity 2018.3~) // C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ => await UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(_ =>
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
}); }, token);
```
`UniTask.WhenEach` that is similar to .NET 9's `Task.WhenEach` can consume new way for await multiple tasks.
```csharp
await foreach (var result in UniTask.WhenEach(task1, task2, task3))
{
// The result is of type WhenEachResult<T>.
// It contains either `T Result` or `Exception Exception`.
// You can check `IsCompletedSuccessfully` or `IsFaulted` to determine whether to access `.Result` or `.Exception`.
// If you want to throw an exception when `IsFaulted` and retrieve the result when successful, use `GetResult()`.
Debug.Log(result.GetResult());
}
``` ```
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks. UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
@@ -925,6 +957,14 @@ public class AsyncMessageBroker<T> : IDisposable
} }
``` ```
vs Awaitable
---
Unity 6 introduces the awaitable type, [Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html). To put it simply, Awaitable can be considered a subset of UniTask, and in fact, Awaitable's design was influenced by UniTask. It should be able to handle PlayerLoop-based awaits, pooled Tasks, and support for cancellation with `CancellationToken` in a similar way. With its inclusion in the standard library, you may wonder whether to continue using UniTask or migrate to Awaitable. Here's a brief guide.
First, the functionality provided by Awaitable is equivalent to what coroutines offer. Instead of `yield return`, you use await; `await NextFrameAsync()` replaces `yield return null`; and there are equivalents for `WaitForSeconds` and `EndOfFrame`. However, that's the extent of it. Being coroutine-based in terms of functionality, it lacks Task-based features. In practical application development using async/await, operations like `WhenAll` are essential. Additionally, UniTask enables many frame-based operations (such as `DelayFrame`) and more flexible PlayerLoopTiming control, which are not available in Awaitable. Of course, there's no Tracker Window either.
Therefore, I recommend using UniTask for application development. UniTask is a superset of Awaitable and includes many essential features. For library development, where you want to avoid external dependencies, using Awaitable as a return type for methods would be appropriate. Awaitable can be converted to UniTask using `AsUniTask`, so there's no issue in handling Awaitable-based functionality within the UniTask library. Of course, if you don't need to worry about dependencies, using UniTask would be the best choice even for library development.
For Unit Testing For Unit Testing
--- ---
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods. Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.
@@ -1014,6 +1054,7 @@ Use UniTask type.
| `Task.Run` | `UniTask.RunOnThreadPool` | | `Task.Run` | `UniTask.RunOnThreadPool` |
| `Task.WhenAll` | `UniTask.WhenAll` | | `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` | | `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.WhenEach` | `UniTask.WhenEach` |
| `Task.CompletedTask` | `UniTask.CompletedTask` | | `Task.CompletedTask` | `UniTask.CompletedTask` |
| `Task.FromException` | `UniTask.FromException` | | `Task.FromException` | `UniTask.FromException` |
| `Task.FromResult` | `UniTask.FromResult` | | `Task.FromResult` | `UniTask.FromResult` |

View File

@@ -2,27 +2,27 @@ UniTask
=== ===
[![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases) [![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases)
为Unity提供一个高性能0GC的async/await异步方案。 为Unity提供一个高性能零堆内存分配的 async/await 异步方案。
- 基于值类型的`UniTask<T>`和自定义的 AsyncMethodBuilder 来实现0GC - 基于值类型的`UniTask<T>`和自定义的 AsyncMethodBuilder 来实现零堆内存分配
- 使所有 Unity 的 AsyncOperations 和 Coroutines 可等待 - 使所有 Unity 的 AsyncOperations 和 Coroutines 可等待
- 基于 PlayerLoop 的任务( `UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) 可以替换所有协程操作 - 基于 PlayerLoop 的任务`UniTask.Yield``UniTask.Delay``UniTask.DelayFrame`等..可以替换所有协程操作
- 对MonoBehaviour 消息事件和 uGUI 事件进行 可等待/异步枚举 - MonoBehaviour 消息事件和 uGUI 事件进行可等待/异步枚举
- 完全在 Unity 的 PlayerLoop 上运行因此不使用Thread并且同样能在 WebGL、wasm 等平台上运行。 - 完全在 Unity 的 PlayerLoop 上运行因此不使用Thread并且同样能在 WebGL、wasm 等平台上运行。
- 带有 Channel 和 AsyncReactiveProperty的异步 LINQ - 带有 Channel 和 AsyncReactiveProperty 的异步 LINQ
- 提供一个 TaskTracker EditorWindow 以追踪所有 UniTask 分配来预防内存泄漏 - 提供一个 TaskTracker EditorWindow 以追踪所有 UniTask 分配来预防内存泄漏
- 与原生 Task/ValueTask/IValueTaskSource 高度兼容的行为 - 与原生 Task/ValueTask/IValueTaskSource 高度兼容的行为
有关技术细节,请参阅博客文章:[UniTask v2 — Unity 的0GC async/await 以及 异步LINQ 的使用](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd) 有关技术细节,请参阅博客文章:[UniTask v2 — 适用于 Unity 的零堆内存分配的async/await,支持异步 LINQ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
有关高级技巧,请参阅博客文章:[通过异步装饰器模式扩展 UnityWebRequest — UniTask 的高级技术](https://medium.com/@neuecc/extends-unitywebrequest-via-async-decorator-pattern-advanced-techniques-of-unitask-ceff9c5ee846) 有关高级技巧,请参阅博客文章:[通过异步装饰器模式扩展 UnityWebRequest — UniTask 的高级技术](https://medium.com/@neuecc/extends-unitywebrequest-via-async-decorator-pattern-advanced-techniques-of-unitask-ceff9c5ee846)
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents ## 目录
- [入门](#%E5%85%A5%E9%97%A8) - [入门](#%E5%85%A5%E9%97%A8)
- [UniTask 和 AsyncOperation 基础知识](#unitask-%E5%92%8C-asyncoperation-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86) - [UniTask 和 AsyncOperation 基础知识](#unitask-%E5%92%8C-asyncoperation-%E7%9A%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86)
- [Cancellation and Exception handling](#cancellation-and-exception-handling) - [取消和异常处理](#%E5%8F%96%E6%B6%88%E5%92%8C%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86)
- [超时处理](#%E8%B6%85%E6%97%B6%E5%A4%84%E7%90%86) - [超时处理](#%E8%B6%85%E6%97%B6%E5%A4%84%E7%90%86)
- [进度](#%E8%BF%9B%E5%BA%A6) - [进度](#%E8%BF%9B%E5%BA%A6)
- [PlayerLoop](#playerloop) - [PlayerLoop](#playerloop)
@@ -32,41 +32,42 @@ UniTask
- [AsyncEnumerable 和 Async LINQ](#asyncenumerable-%E5%92%8C-async-linq) - [AsyncEnumerable 和 Async LINQ](#asyncenumerable-%E5%92%8C-async-linq)
- [可等待事件](#%E5%8F%AF%E7%AD%89%E5%BE%85%E4%BA%8B%E4%BB%B6) - [可等待事件](#%E5%8F%AF%E7%AD%89%E5%BE%85%E4%BA%8B%E4%BB%B6)
- [Channel](#channel) - [Channel](#channel)
- [与 Awaitable 对比](#%E4%B8%8E-awaitable-%E5%AF%B9%E6%AF%94)
- [单元测试](#%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95) - [单元测试](#%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95)
- [线程池限制](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E9%99%90%E5%88%B6) - [线程池限制](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E9%99%90%E5%88%B6)
- [IEnumerator.ToUniTask 限制](#ienumeratortounitask-%E9%99%90%E5%88%B6) - [IEnumerator.ToUniTask 限制](#ienumeratortounitask-%E7%9A%84%E9%99%90%E5%88%B6)
- [关于UnityEditor](#%E5%85%B3%E4%BA%8Eunityeditor) - [关于 UnityEditor](#%E5%85%B3%E4%BA%8E-unityeditor)
- [与原生Task API对比](#%E4%B8%8E%E5%8E%9F%E7%94%9Ftask-api%E5%AF%B9%E6%AF%94) - [与原生 Task API 对比](#%E4%B8%8E%E5%8E%9F%E7%94%9F-task-api-%E5%AF%B9%E6%AF%94)
- [池化配置](#%E6%B1%A0%E5%8C%96%E9%85%8D%E7%BD%AE) - [池化配置](#%E6%B1%A0%E5%8C%96%E9%85%8D%E7%BD%AE)
- [Profiler下的分配](#profiler%E4%B8%8B%E7%9A%84%E5%88%86%E9%85%8D) - [Profiler 下的堆内存分配](#profiler-%E4%B8%8B%E7%9A%84%E5%A0%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D)
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext) - [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
- [API References](#api-references) - [API 文档](#api-%E6%96%87%E6%A1%A3)
- [UPM Package](#upm-package) - [UPM ](#upm-%E5%8C%85)
- [通过 git URL 安装](#%E9%80%9A%E8%BF%87-git-url-%E5%AE%89%E8%A3%85) - [通过 git URL 安装](#%E9%80%9A%E8%BF%87-git-url-%E5%AE%89%E8%A3%85)
- [通过 OpenUPM 安装](#%E9%80%9A%E8%BF%87-openupm-%E5%AE%89%E8%A3%85) - [关于 .NET Core](#%E5%85%B3%E4%BA%8E-net-core)
- [.NET Core](#net-core) - [许可证](#%E8%AE%B8%E5%8F%AF%E8%AF%81)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->
入门 入门
--- ---
通过[UniTask/releases](https://github.com/Cysharp/UniTask/releases)页面中提供的[UPM 包](https://github.com/Cysharp/UniTask#upm-package)或资产包 ( `UniTask.*.*.*.unitypackage`)安装。 通过[UniTask/releases](https://github.com/Cysharp/UniTask/releases)页面中提供的[UPM 包](https://github.com/Cysharp/UniTask#upm-package)或资产包`UniTask.*.*.*.unitypackage`安装。
```csharp ```csharp
// 使用 UniTask 所需的命名空间 // 使用 UniTask 所需的命名空间
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
// 可以返回一个形如 UniTask<T>(或 UniTask) 的类型这种类型事为Unity定制的作为替代原生Task<T>的轻量级方案 // 可以返回一个形如 UniTask<T>(或 UniTask) 的类型这种类型事为Unity定制的作为替代原生 Task<T> 的轻量级方案
// 为Unity集成的 0GC快速调用0消耗的 async/await 方案 // 为 Unity 集成的零堆内存分配快速调用0消耗的 async/await 方案
async UniTask<string> DemoAsync() async UniTask<string> DemoAsync()
{ {
// 可以等待一个Unity异步对象 // 可以等待一个 Unity 异步对象
var asset = await Resources.LoadAsync<TextAsset>("foo"); var asset = await Resources.LoadAsync<TextAsset>("foo");
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text; var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
await SceneManager.LoadSceneAsync("scene2"); await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation 会启用取消功能GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的 Cancel 句柄,当对象被销毁时,将会调用这个 Cancel 句柄,从而实现取消的功能 // .WithCancellation 会启用取消功能GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的 Cancel 句柄,当对象被销毁时,将会调用这个 Cancel 句柄,从而实现取消的功能
// 在 Unity 2022.2之后,您可以在 MonoBehaviour 中使用`destroyCancellationToken`
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask 可接收一个 progress 回调以及一些配置参数Progress.Create 是 IProgress<T> 的轻量级替代方案 // .ToUniTask 可接收一个 progress 回调以及一些配置参数Progress.Create 是 IProgress<T> 的轻量级替代方案
@@ -78,29 +79,34 @@ async UniTask<string> DemoAsync()
// yield return new WaitForSeconds/WaitForSecondsRealtime 的替代方案 // yield return new WaitForSeconds/WaitForSecondsRealtime 的替代方案
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false); await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
// 可以等待任何 playerloop 的生命周期(PreUpdate, Update, LateUpdate, 等...) // 可以等待任何 playerloop 的生命周期PreUpdateUpdateLateUpdate等)
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate); await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
// yield return null 替代方案 // yield return null 替代方案
await UniTask.Yield(); await UniTask.Yield();
await UniTask.NextFrame(); await UniTask.NextFrame();
// WaitForEndOfFrame 替代方案 (需要 MonoBehaviour(CoroutineRunner)) // WaitForEndOfFrame 替代方案
#if UNITY_2023_1_OR_NEWER
await UniTask.WaitForEndOfFrame();
#else
// 需要 MonoBehaviourCoroutineRunner
await UniTask.WaitForEndOfFrame(this); // this是一个 MonoBehaviour await UniTask.WaitForEndOfFrame(this); // this是一个 MonoBehaviour
#endif
// yield return new WaitForFixedUpdate 替代方案,(和 UniTask.Yield(PlayerLoopTiming.FixedUpdate) 效果一样) // yield return new WaitForFixedUpdate 替代方案,(等同于 UniTask.Yield(PlayerLoopTiming.FixedUpdate)
await UniTask.WaitForFixedUpdate(); await UniTask.WaitForFixedUpdate();
// yield return WaitUntil 替代方案 // yield return WaitUntil 替代方案
await UniTask.WaitUntil(() => isActive == false); await UniTask.WaitUntil(() => isActive == false);
// WaitUntil展,指定某个值改变时触发 // WaitUntil展,指定某个值改变时触发
await UniTask.WaitUntilValueChanged(this, x => x.isActive); await UniTask.WaitUntilValueChanged(this, x => x.isActive);
// 可以直接 await 一个 IEnumerator 协程 // 可以直接 await 一个 IEnumerator 协程
await FooCoroutineEnumerator(); await FooCoroutineEnumerator();
// 可以直接 await 一个原生 task // 可以直接 await 一个原生 task
await Task.Run(() => 100); await Task.Run(() => 100);
// 多线程示例,在此行代码后的内容都运行在一个线程池上 // 多线程示例,在此行代码后的内容都运行在一个线程池上
@@ -108,7 +114,7 @@ async UniTask<string> DemoAsync()
/* 工作在线程池上的代码 */ /* 工作在线程池上的代码 */
// 转回主线程 // 转回主线程(等同于 UniRx 的`ObserveOnMainThread`
await UniTask.SwitchToMainThread(); await UniTask.SwitchToMainThread();
// 获取异步的 webrequest // 获取异步的 webrequest
@@ -125,37 +131,37 @@ async UniTask<string> DemoAsync()
// 构造一个 async-wait并通过元组语义轻松获取所有结果 // 构造一个 async-wait并通过元组语义轻松获取所有结果
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
// WhenAll简写形式 // WhenAll简写形式,元组可以直接 await
var (google2, bing2, yahoo2) = await (task1, task2, task3); var (google2, bing2, yahoo2) = await (task1, task2, task3);
// 返回一个异步值,或者也可以使用`UniTask`(无结果), `UniTaskVoid`(协程,不可等待) // 返回一个异步值,或者也可以使用`UniTask`无结果`UniTaskVoid`不可等待
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found"); return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
} }
``` ```
UniTask 和 AsyncOperation 基础知识 UniTask 和 AsyncOperation 基础知识
--- ---
UniTask 功能依赖于 C# 7.0( [task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md) ) 所以需要的 Unity 最低版本是`Unity 2018.3` ,官方支持的最低版本是`Unity 2018.4.13f1`. UniTask 功能依赖于 C# 7.0[task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)所以需要`Unity 2018.3`之后的版本,官方支持的最低版本是`Unity 2018.4.13f1`
为什么需要 UniTask自定义task对象因为原生 Task 太重,与 Unity 线程单线程相性不好。UniTask 不使用线程和 SynchronizationContext/ExecutionContext,因为 Unity 的异步对象由 Unity 的引擎层自动调度。它实现了更快和更低的分配并且与Unity完全兼容。 为什么需要 UniTask自定义task对象因为原生 Task 太重,与 Unity 线程(单线程)相性不好。因为 Unity 的异步对象由 Unity 的引擎层自动调度,所以 UniTask 不使用线程和 SynchronizationContext/ExecutionContext。它实现了更快和更低的分配并且与Unity完全兼容。
可以在使用 `using Cysharp.Threading.Tasks;`时对 `AsyncOperation` `ResourceRequest``AssetBundleRequest` `AssetBundleCreateRequest` `UnityWebRequestAsyncOperation` `AsyncGPUReadbackRequest` `IEnumerator`以及其他的异步操作进行 await 可以在使用`using Cysharp.Threading.Tasks;`时对`AsyncOperation``ResourceRequest``AssetBundleRequest``AssetBundleCreateRequest``UnityWebRequestAsyncOperation``AsyncGPUReadbackRequest``IEnumerator`以及其他的异步操作进行 await
UniTask 提供了三种模式的扩展方法。 UniTask 提供了三种模式的扩展方法。
```csharp ```csharp
* await asyncOperation; await asyncOperation;
* .WithCancellation(CancellationToken); .WithCancellation(CancellationToken);
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken); .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
``` ```
`WithCancellation``ToUniTask`的简化版本,两者都返回`UniTask`。有关 cancellation 的详细信息,请参阅:[取消和异常处理](https://github.com/Cysharp/UniTask#cancellation-and-exception-handling)部分。 `WithCancellation``ToUniTask`的简化版本,两者都返回`UniTask`。有关 cancellation 的详细信息,请参阅:[取消和异常处理](https://github.com/Cysharp/UniTask#cancellation-and-exception-handling)部分。
> 注意await 会在 PlayerLoop 执行await对象的相应native生命周期方法时返回如果条件满足的话而 WithCancellation 和 ToUniTask 是从指定的 PlayerLoop 生命周期执行时返回。有关 PlayLoop生命周期 的详细信息,请参阅:[PlayerLoop](https://github.com/Cysharp/UniTask#playerloop)部分。 > 注意await 会在 PlayerLoop 执行await对象的相应native生命周期方法时返回如果条件满足的话而 WithCancellation 和 ToUniTask 是从指定的 PlayerLoop 生命周期执行时返回。有关 PlayLoop生命周期 的详细信息,请参阅:[PlayerLoop](https://github.com/Cysharp/UniTask#playerloop)部分。
> 注意: AssetBundleRequest 有`asset`和`allAssets`,默认 await 返回`asset`。如果想得到`allAssets`可以使用`AwaitForAllAssets()`方法。 > 注意: AssetBundleRequest 有`asset`和`allAssets`,默认 await 返回`asset`。如果想得到`allAssets`可以使用`AwaitForAllAssets()`方法。
`UniTask`可以使用`UniTask.WhenAll``UniTask.WhenAny`等实用函数。它们就像`Task.WhenAll`/`Task.WhenAny`但它们返回内容,这很有用。它们会返回值元组,因此您可以传递多种类型并解构每个结果。 `UniTask`可以使用`UniTask.WhenAll``UniTask.WhenAny``UniTask.WhenEach`等实用函数。它们就像`Task.WhenAll``Task.WhenAny`但它们返回的数据类型更好用。它们会返回值元组,因此您可以传递多种类型并解构每个结果。
```csharp ```csharp
public async UniTaskVoid LoadManyAsync() public async UniTaskVoid LoadManyAsync()
@@ -174,7 +180,7 @@ async UniTask<Sprite> LoadAsSprite(string path)
} }
``` ```
如果你想转换一个回调逻辑块,让它变成UniTask的话,可以使用 `UniTaskCompletionSource<T>` `TaskCompletionSource<T>`的轻量级魔改版) 如果您想要将一个回调转换为 UniTask可以使用`UniTaskCompletionSource<T>`,它是`TaskCompletionSource<T>`的轻量级版本。
```csharp ```csharp
public UniTask<int> WrapByUniTaskCompletionSource() public UniTask<int> WrapByUniTaskCompletionSource()
@@ -182,47 +188,43 @@ public UniTask<int> WrapByUniTaskCompletionSource()
var utcs = new UniTaskCompletionSource<int>(); var utcs = new UniTaskCompletionSource<int>();
// 当操作完成时,调用 utcs.TrySetResult(); // 当操作完成时,调用 utcs.TrySetResult();
// 当操作失败时, 调用 utcs.TrySetException(); // 当操作失败时调用 utcs.TrySetException();
// 当操作取消时, 调用 utcs.TrySetCanceled(); // 当操作取消时调用 utcs.TrySetCanceled();
return utcs.Task; //本质上就是返回了一个 UniTask<int> return utcs.Task; //本质上就是返回了一个 UniTask<int>
} }
``` ```
您可以进行如下转换 您可以进行如下转换<br>-`Task` -> `UniTask `:使用`AsUniTask`<br>-`UniTask` -> `UniTask<AsyncUnit>`:使用 `AsAsyncUnitUniTask`<br>-`UniTask<T>` -> `UniTask`:使用 `AsUniTask``UniTask<T>` -> `UniTask`的转换是无消耗的。
- `Task` -> `UniTask `: 使用`AsUniTask` 如果您想将异步转换为协程,您可以使用`.ToCoroutine()`,这对于您想只允许使用协程系统大有帮助。
- `UniTask` -> `UniTask<AsyncUnit>`: 使用 `AsAsyncUnitUniTask`
- `UniTask<T>` -> `UniTask`: 使用 `AsUniTask`,这两者的转换是无消耗的
如果你想将异步转换为协程,你可以使用`.ToCoroutine()`,如果你只想允许使用协程系统,这很有用 UniTask 不能 await 两次。这是与.NET Standard 2.1 中引入的[ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1)具有相同的约束
UniTask 不能await两次。这是与.NET Standard 2.1 中引入的[ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1)相同的约束。 > 千万不要对 `ValueTask<TResult>` 实例执行以下操作:
> 永远不应在 ValueTask 实例上执行以下操作:
> >
> - 多次await实例。 > - 多次await实例。
> - 多次调用 AsTask。 > - 多次调用 AsTask。
> - 在操作尚未完成时调用 .Result 或 .GetAwaiter().GetResult(),多次调用也是不允许的 > - 在操作尚未完成时调用 .Result 或 .GetAwaiter().GetResult()或对它们进行多次调用。
> - 混用上述行为更是不被允许的 > - 对实例进行上述多种操作
> >
> 如果您执行上述任何操作,则结果是未定义。 > 如果您执行上述任何操作,则结果是未定义
```csharp ```csharp
var task = UniTask.DelayFrame(10); var task = UniTask.DelayFrame(10);
await task; await task;
await task; // 寄了, 抛出异常 await task; // 错误,抛出异常
``` ```
如果实在需要多次await一个异步操作可以使用`UniTask.Lazy`来支持多次调用`.Preserve()`同样允许多次调用由UniTask内部缓存结果)。这种方法在函数范围内有多调用时很有用。 如果实在需要多次 await 一个异步操作,可以使用支持多次调用的`UniTask.Lazy``.Preserve()`同样允许多次调用(由 UniTask 内部缓存结果)。这种方法在函数范围内有多调用时很有用。
同样的`UniTaskCompletionSource`可以在同一个地方被await多次或者在很多不同的地方被await。 同样的`UniTaskCompletionSource`可以在同一个地方被 await 多次,或者在很多不同的地方被 await。
Cancellation and Exception handling 取消和异常处理
--- ---
一些 UniTask 工厂方法有一个`CancellationToken cancellationToken = default`参数。Unity 的一些异步操作也有`WithCancellation(CancellationToken)``ToUniTask(..., CancellationToken cancellation = default)`展方法。 一些 UniTask 工厂方法有一个`CancellationToken cancellationToken = default`参数。Unity 的一些异步操作也有`WithCancellation(CancellationToken)``ToUniTask(..., CancellationToken cancellation = default)`展方法。
可以传递原生[`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource)给参数CancellationToken 可以通过原生[`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource)CancellationToken 传递给参数
```csharp ```csharp
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -237,14 +239,14 @@ await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellati
await UniTask.DelayFrame(1000, cancellationToken: cts.Token); await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
``` ```
CancellationToken 可以由`CancellationTokenSource`或 MonoBehaviour 的`GetCancellationTokenOnDestroy`扩展方法创建。 CancellationToken 可通过`CancellationTokenSource`或 MonoBehaviour 的扩展方法`GetCancellationTokenOnDestroy`创建。
```csharp ```csharp
// 这个CancellationTokenSource和this GameObject生命周期相同当this GameObject Destroy的时候就会执行Cancel // 这个 CancellationToken 的生命周期与 GameObject 的相同
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy()); await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
``` ```
对于链式取消,所有异步方法都建议最后一个参数接受`CancellationToken cancellationToken`,并将`CancellationToken`从头传递到尾。 对于链式取消,建议所有异步方法最后一个参数接受`CancellationToken cancellationToken`,并将`CancellationToken`从头传递到尾。
```csharp ```csharp
await FooAsync(this.GetCancellationTokenOnDestroy()); await FooAsync(this.GetCancellationTokenOnDestroy());
@@ -262,7 +264,7 @@ async UniTask BarAsync(CancellationToken cancellationToken)
} }
``` ```
`CancellationToken`表示异步的生命周期。您可以使用自定义的生命周期,而不是默认的 CancellationTokenOnDestroy。 `CancellationToken`代表了异步操作的生命周期。您可以使用默认的 CancellationTokenOnDestroy ,通过自定义的`CancellationToken`自行管理生命周期
```csharp ```csharp
public class MyBehaviour : MonoBehaviour public class MyBehaviour : MonoBehaviour
@@ -292,9 +294,11 @@ public class MyBehaviour : MonoBehaviour
} }
``` ```
当检测到取消时,所有方法都会向上游抛出并传播`OperationCanceledException`。当异常(不限于`OperationCanceledException`)没有在异步方法中处理时,它将最终传播到`UniTaskScheduler.UnobservedTaskException`。接收到的未处理异常的默认行为是将日志写入异常。可以使用`UniTaskScheduler.UnobservedExceptionWriteLogType`更改日志级别。如果要使用自定义行为,请为`UniTaskScheduler.UnobservedTaskException.`设置一个委托 在Unity 2022.2之后Unity在[MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html)和[Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html)中添加了 CancellationToken。
`OperationCanceledException`是一个特殊的异常,会被`UnobservedTaskException`.无视 当检测到取消时,所有方法都会向上游抛出并传播`OperationCanceledException`。当异常(不限于`OperationCanceledException`)没有在异步方法中处理时,它将被传播到`UniTaskScheduler.UnobservedTaskException`。默认情况下,将接收到的未处理异常作为一般异常写入日志。可以使用`UniTaskScheduler.UnobservedExceptionWriteLogType`更改日志级别。若想对接收到未处理异常时的处理进行自定义,请为`UniTaskScheduler.UnobservedTaskException`设置一个委托
`OperationCanceledException`是一种特殊的异常,会被`UnobservedTaskException`无视
如果要取消异步 UniTask 方法中的行为,请手动抛出`OperationCanceledException` 如果要取消异步 UniTask 方法中的行为,请手动抛出`OperationCanceledException`
@@ -306,7 +310,7 @@ public async UniTask<int> FooAsync()
} }
``` ```
如果您处理异常但想忽略(传播到全局cancellation处理的地方),请使用异常过滤器。 如果您只想处理异常,忽略取消操作(让其传播到全局处理 cancellation 的地方),请使用异常过滤器。
```csharp ```csharp
public async UniTask<int> BarAsync() public async UniTask<int> BarAsync()
@@ -316,14 +320,14 @@ public async UniTask<int> BarAsync()
var x = await FooAsync(); var x = await FooAsync();
return x * 2; return x * 2;
} }
catch (Exception ex) when (!(ex is OperationCanceledException)) // when (ex is not OperationCanceledException) at C# 9.0 catch (Exception ex) when (!(ex is OperationCanceledException)) // 在 C# 9.0 下改成 when (ex is not OperationCanceledException)
{ {
return -1; return -1;
} }
} }
``` ```
throws/catch`OperationCanceledException`有点重,所以如果性能是一个问题,请使用`UniTask.SuppressCancellationThrow`以避免 OperationCanceledException 抛出。它将返回`(bool IsCanceled, T Result)`而不是抛出。 抛出和捕获`OperationCanceledException`有点重度,如果比较在意性能开销,请使用`UniTask.SuppressCancellationThrow`以避免抛出 OperationCanceledException 。它将返回`(bool IsCanceled, T Result)`而不是抛出异常
```csharp ```csharp
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow(); var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
@@ -333,7 +337,19 @@ if (isCanceled)
} }
``` ```
注意:仅当您在原方法直接调用SuppressCancellationThrow时才会抑制异常抛出。否则返回值将被转换且整个管道不会抑制 throws 注意:仅当您在源头处直接调用`UniTask.SuppressCancellationThrow`时才会抑制异常抛出。否则,返回值将被转换,且整个管道不会抑制异常抛出
`UniTask.Yield``UniTask.Delay`等功能依赖于 Unity 的 PlayerLoop它们在 PlayerLoop 中确定`CancellationToken`状态。
这意味着当`CancellationToken`被触发时,它们并不会立即取消。
如果要更改此行为,实现立即取消,可将`cancelImmediately`标志设置为 true。
```csharp
await UniTask.Yield(cancellationToken, cancelImmediately: true);
```
注意:比起默认行为,设置 `cancelImmediately` 为 true 并检测立即取消会有更多的性能开销。
这是因为它使用了`CancellationToken.Register`;这比在 PlayerLoop 中检查 CancellationToken 更重度。
超时处理 超时处理
--- ---
@@ -341,7 +357,7 @@ if (isCanceled)
```csharp ```csharp
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout. cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 设置5s超时。
try try
{ {
@@ -356,19 +372,19 @@ catch (OperationCanceledException ex)
} }
``` ```
> > `CancellationTokenSouce.CancelAfter`是一个原生的api。但是在 Unity 中不应该使用它,因为它依赖于线程计时器。`CancelAfterSlim`是 UniTask 的扩展方法,它使用 PlayerLoop 代替。 > `CancellationTokenSouce.CancelAfter`是一个原生的 api。但是在 Unity 中不应该使用它,因为它依赖于线程计时器。`CancelAfterSlim`是 UniTask 的扩展方法,它使用 PlayerLoop 代替了线程计时器
>
> 如果您想将超时与其他cancellation一起使用请使用`CancellationTokenSource.CreateLinkedTokenSource`. 如果您想将超时与其他 cancellation 一起使用,请使用`CancellationTokenSource.CreateLinkedTokenSource`
```csharp ```csharp
var cancelToken = new CancellationTokenSource(); var cancelToken = new CancellationTokenSource();
cancelButton.onClick.AddListener(()=> cancelButton.onClick.AddListener(()=>
{ {
cancelToken.Cancel(); // 点击按钮后取消 cancelToken.Cancel(); // 点击按钮后取消
}); });
var timeoutToken = new CancellationTokenSource(); var timeoutToken = new CancellationTokenSource();
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 设置5s超时 timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 设置5s超时
try try
{ {
@@ -390,19 +406,19 @@ catch (OperationCanceledException ex)
} }
``` ```
优化减少每调用异步方法超时的 CancellationTokenSource 分配,您可以使用 UniTask 的`TimeoutController`. 为减少每调用异步方法时用于超时的 CancellationTokenSource 的堆内存分配,您可以使用 UniTask 的`TimeoutController`进行优化。
```csharp ```csharp
TimeoutController timeoutController = new TimeoutController(); // 复用timeoutController TimeoutController timeoutController = new TimeoutController(); // 提前创建好,以便复用。
async UniTask FooAsync() async UniTask FooAsync()
{ {
try try
{ {
// 可以通过 timeoutController.Timeout(TimeSpan) 传递到 cancellationToken. // 可以通过 timeoutController.Timeout(TimeSpan) 把超时设置传递到 cancellationToken
await UnityWebRequest.Get("http://foo").SendWebRequest() await UnityWebRequest.Get("http://foo").SendWebRequest()
.WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5))); .WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5)));
timeoutController.Reset(); // 当await完成后调用Reset停止超时计时器并准备下一次复用 timeoutController.Reset(); // 当 await 完成后调用 Reset停止超时计时器并准备下一次复用
} }
catch (OperationCanceledException ex) catch (OperationCanceledException ex)
{ {
@@ -414,7 +430,7 @@ async UniTask FooAsync()
} }
``` ```
如果您想将超时其他取消源一起使用,请使用`new TimeoutController(CancellationToken)`. 如果您想将超时结合其他取消源一起使用,请使用`new TimeoutController(CancellationToken)`.
```csharp ```csharp
TimeoutController timeoutController; TimeoutController timeoutController;
@@ -427,11 +443,11 @@ void Start()
} }
``` ```
注意UniTask 有`.Timeout`,`.TimeoutWithoutException`方法,但是,如果可能,不要使用这些,请通过`CancellationToken`. 由于`.Timeout`作用在task外部无法停止超时任务。`.Timeout`表示超时忽略结果。如果您将一个`CancellationToken`传递给该方法,它将从任务内部执行,因此可以停止正在运行的任务。 注意UniTask 有`.Timeout``.TimeoutWithoutException`方法,但如果可以的话,尽量不要使用这些方法,请传递`CancellationToken`。因为`.Timeout`是在任务外部执行,所以无法停止超时任务。`.Timeout`意味着超时忽略结果。如果您将一个`CancellationToken`传递给该方法,它将从任务内部执行,因此可以停止正在运行的任务。
进度 进度
--- ---
一些Unity的异步操作具有`ToUniTask(IProgress<float> progress = null, ...)`扩展方法。 一些 Unity 的异步操作具有`ToUniTask(IProgress<float> progress = null, ...)`扩展方法。
```csharp ```csharp
var progress = Progress.Create<float>(x => Debug.Log(x)); var progress = Progress.Create<float>(x => Debug.Log(x));
@@ -441,9 +457,9 @@ var request = await UnityWebRequest.Get("http://google.co.jp")
.ToUniTask(progress: progress); .ToUniTask(progress: progress);
``` ```
您不应该使用原生的`new System.Progress<T>`,因为每次都会导致GC分配。改为使`Cysharp.Threading.Tasks.Progress`。这个 progress factory 有两个方法,`Create``CreateOnlyValueChanged`. `CreateOnlyValueChanged`仅在进度值更新时调用。 您不应该使用原生的`new System.Progress<T>`,因为每次调用它都会产生堆内存分配。请改`Cysharp.Threading.Tasks.Progress`。这个 progress 工厂类有两个方法,`Create``CreateOnlyValueChanged``CreateOnlyValueChanged`仅在进度值更新时调用。
为调用者实现 IProgress 接口会更好,因为这样可以没有 lambda 分配。 为调用者实现 IProgress 接口会更好,这样不会因使用 lambda 而产生堆内存分配。
```csharp ```csharp
public class Foo : MonoBehaviour, IProgress<float> public class Foo : MonoBehaviour, IProgress<float>
@@ -464,7 +480,7 @@ public class Foo : MonoBehaviour, IProgress<float>
PlayerLoop PlayerLoop
--- ---
UniTask 在自定义[PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html)上运行。UniTask 基于 playerloop 的方法(`Delay``DelayFrame``asyncOperation.ToUniTask`等)接受这个`PlayerLoopTiming` UniTask 运行在自定义[PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html)。UniTask 基于 PlayerLoop 的方法(如`Delay``DelayFrame``asyncOperation.ToUniTask`等)接受这个`PlayerLoopTiming`
```csharp ```csharp
public enum PlayerLoopTiming public enum PlayerLoopTiming
@@ -497,27 +513,32 @@ public enum PlayerLoopTiming
} }
``` ```
它表示何时运行,您可以查[PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) Unity 的默认 playerloop 注入 UniTask 的自定义循环。 它表明了异步任务会在哪个时机运行,您可以查[PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae)以了解 Unity 的默认 PlayerLoop 以及注入 UniTask 的自定义循环。
`PlayerLoopTiming.Update`与协程中的`yield return null`类似,但Update(Update 和 uGUI 事件(button.onClick, etc...) 前被调用(在`ScriptRunBehaviourUpdate`时被调用),yield return null 在`ScriptRunDelayedDynamicFrameRate`时被调用。`PlayerLoopTiming.FixedUpdate`类似于`WaitForFixedUpdate` `PlayerLoopTiming.Update`与协程中的`yield return null`类似,但它会在`ScriptRunBehaviourUpdate`时,UpdateUpdate 和 uGUI 事件(button.onClick等)之前被调用,而 yield return null `ScriptRunDelayedDynamicFrameRate`时被调用。`PlayerLoopTiming.FixedUpdate`类似于`WaitForFixedUpdate`
> `PlayerLoopTiming.LastPostLateUpdate`不等同于协程的`yield return new WaitForEndOfFrame()`. 协程的 WaitForEndOfFrame 似乎在 PlayerLoop 完成后运行。一些需要协程结束帧(`Texture2D.ReadPixels`, `ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, 等) 的方法在 async/await 时无法正常工作。在这些情况下,请将 MonoBehaviour(coroutine runner) 传递给`UniTask.WaitForEndOfFrame`. 例如,`await UniTask.WaitForEndOfFrame(this);`是`yield return new WaitForEndOfFrame()`轻量级0GC的替代方案。 > `PlayerLoopTiming.LastPostLateUpdate`不等同于协程的`yield return new WaitForEndOfFrame()`协程的 WaitForEndOfFrame 似乎在 PlayerLoop 完成后运行。一些需要协程结束帧的方法(`Texture2D.ReadPixels``ScreenCapture.CaptureScreenshotAsTexture``CommandBuffer`等)在 async/await 时无法正常工作。在这些情况下,请将 MonoBehaviour(用于运行协程)传递给`UniTask.WaitForEndOfFrame`例如,`await UniTask.WaitForEndOfFrame(this);`是`yield return new WaitForEndOfFrame()`轻量级无堆内存分配的替代方案。
`yield return null``UniTask.Yield`相似但不同。`yield return null`总是返回下一帧但`UniTask.Yield`返回下一个调用。也就是说,`UniTask.Yield(PlayerLoopTiming.Update)``PreUpdate`上调用,它返回相同的帧。`UniTask.NextFrame()`保证返回下一帧,您可以认为它的行为与`yield return null`一致. > 注意:在 Unity 2023.1或更高的版本中,`await UniTask.WaitForEndOfFrame();`不再需要 MonoBehaviour。因为它使用了`UnityEngine.Awaitable.EndOfFrameAsync`。
> UniTask.Yield(without CancellationToken) 是一种特殊类型,返回`YieldAwaitable`并在 YieldRunner 上运行。它是最轻量和最快的 `yield return null``UniTask.Yield`相似但不同。`yield return null`总是返回下一帧但`UniTask.Yield`返回下一次调用。也就是说,`UniTask.Yield(PlayerLoopTiming.Update)``PreUpdate`上调用,它返回同一帧。`UniTask.NextFrame()`保证返回下一帧,您可以认为它的行为与`yield return null`一致
`AsyncOperation`在原生生命周期返回。例如await `SceneManager.LoadSceneAsync``EarlyUpdate.UpdatePreloading`时返回,在此之后,加载的场景的`Start`方法调用自`EarlyUpdate.ScriptRunDelayedStartupFrame`。同样的,`await UnityWebRequest``EarlyUpdate.ExecuteMainThreadJobs`时返回. > UniTask.Yield不带 CancellationToken是一种特殊类型返回`YieldAwaitable`并在 YieldRunner 上运行。它是最轻量和最快的。
在 UniTask 中await 直接使用原生生命周期,`WithCancellation``ToUniTask`可以指定使用的原生生命周期。这通常不会有问题,但是`LoadSceneAsync`在等待之后,它会导致开始和继续的不同顺序。所以建议不要使用`LoadSceneAsync.ToUniTask` `AsyncOperation`在原生生命周期返回。例如await `SceneManager.LoadSceneAsync``EarlyUpdate.UpdatePreloading`时返回,在此之后,在`EarlyUpdate.ScriptRunDelayedStartupFrame`时调用已加载场景的`Start`方法。同样的,`await UnityWebRequest``EarlyUpdate.ExecuteMainThreadJobs`时返回
堆栈跟踪中,您可以检查它在 playerloop 中的运行位置 UniTask 中,直接 await 使用的是原生生命周期,而`WithCancellation``ToUniTask`使用的特定的生命周期。这通常不会有问题,但对于`LoadSceneAsync`,它会导致`Start`方法与 await 之后的逻辑的执行顺序错乱。所以建议不要使用`LoadSceneAsync.ToUniTask`
> 注意:在 Unity 2023.1或更高的版本中,当您使用新的`UnityEngine.Awaitable`方法(如`SceneManager.LoadSceneAsync`)时,请确保您的文件的 using 指令区域中包含`using UnityEngine;`。
> 这可以通过避免使用`UnityEngine.AsyncOperation`版本来防止编译错误。
在堆栈跟踪中,您可以检查它在 PlayerLoop 中的运行位置。
![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png) ![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png)
默认情况下UniTask 的 PlayerLoop 初始化`[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`. 默认情况下UniTask 的 PlayerLoop 在`[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`初始化。
在 BeforeSceneLoad 中调用方法顺序是不确定的,所以如果想在其他 BeforeSceneLoad 方法中使用 UniTask应该尝试在此之前初始化 在 BeforeSceneLoad 中调用方法,它们的执行顺序是不确定的,所以如果想在其他 BeforeSceneLoad 方法中使用 UniTask应该尝试在此之前初始化好 PlayerLoop
```csharp ```csharp
// AfterAssembliesLoaded 表示将会在 BeforeSceneLoad 之前调用 // AfterAssembliesLoaded 表示将会在 BeforeSceneLoad 之前调用
@@ -529,15 +550,15 @@ public static void InitUniTaskLoop()
} }
``` ```
如果您导入 Unity 的`Entities`包,则会将自定义playerloop重置为默认值`BeforeSceneLoad`并注入 ECS 的循环。当 Unity 在 UniTask 的 initialize 方法之后调用 ECS 的 inject 方法UniTask 将不再工作 如果您导入 Unity 的`Entities`包,则会`BeforeSceneLoad`将自定义 PlayerLoop 重置为默认值,并注入 ECS 的循环。当 Unity 在 UniTask 的初始化方法执行之后调用 ECS 的注入方法UniTask 将不再起作用
为了解决这个问题,您可以在 ECS 初始化后重新初始化 UniTask PlayerLoop。 为了解决这个问题,您可以在 ECS 初始化后重新初始化 UniTask PlayerLoop。
```csharp ```csharp
// 获取ECS Loop. // 获取 ECS Loop
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop; var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
// 设置UniTask PlayerLoop // 设置 UniTask PlayerLoop
PlayerLoopHelper.Initialize(ref playerLoop); PlayerLoopHelper.Initialize(ref playerLoop);
``` ```
@@ -551,16 +572,16 @@ void Start()
} }
``` ```
您可以通过除未使用的 PlayerLoopTiming 注入来稍微优化循环成本。您可以在初始化时调用`PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)` 您可以通过除未使用的 PlayerLoopTiming 注入来稍微优化循环成本。您可以在初始化时调用`PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)`
```csharp ```csharp
var loop = PlayerLoop.GetCurrentPlayerLoop(); var loop = PlayerLoop.GetCurrentPlayerLoop();
PlayerLoopHelper.Initialize(ref loop, InjectPlayerLoopTimings.Minimum); // 最小化 is Update | FixedUpdate | LastPostLateUpdate PlayerLoopHelper.Initialize(ref loop, InjectPlayerLoopTimings.Minimum); // Minimum 就是 Update | FixedUpdate | LastPostLateUpdate
``` ```
`InjectPlayerLoopTimings`有三个预设,`All``Standard`(除 LastPostLateUpdate 外),`Minimum``Update | FixedUpdate | LastPostLateUpdate`)。默认为全部,您可以组合自定义注入时间,例如`InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate`. `InjectPlayerLoopTimings`有三个预设,`All``Standard`All 除 LastPostLateUpdate 外),`Minimum``Update | FixedUpdate | LastPostLateUpdate`)。默认为 All,您可以通过组合自定义注入的时机,例如`InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate`
使用未注入`PlayerLoopTiming`的[Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md)可能会出错。例如,您可以为`InjectPlayerLoopTimings.Minimum`设置`BannedSymbols.txt` 使用未注入`PlayerLoopTiming`的[Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md)可能会出错。例如,您可以像下列方式那样,`InjectPlayerLoopTimings.Minimum`设置`BannedSymbols.txt`
```txt ```txt
F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project. F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project.
@@ -578,13 +599,13 @@ F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this Playe
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project. F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project.
``` ```
您可以将`RS0030`严重性配置为错误。 您可以将`RS0030`严重性配置为错误。
![image](https://user-images.githubusercontent.com/46207/109150837-bb933880-77ac-11eb-85ba-4fd15819dbd0.png) ![image](https://user-images.githubusercontent.com/46207/109150837-bb933880-77ac-11eb-85ba-4fd15819dbd0.png)
async void 与 async UniTaskVoid 对比 async void 与 async UniTaskVoid 对比
--- ---
`async void`是一个原生的 C# 任务系统,因此它不在 UniTask 系统上运行。也最好不要使用它。`async UniTaskVoid``async UniTask`的轻量级版本,因为它没有等待完成并立即向`UniTaskScheduler.UnobservedTaskException`报告错误. 如果您不需要等待(即发即弃),那么使用`UniTaskVoid`会更好。不幸的是,要解除警告,您需要在尾部添加`Forget()`. `async void`是一个原生的 C# 任务系统,因此它不在 UniTask 系统上运行。也最好不要使用它。`async UniTaskVoid``async UniTask`的轻量级版本,因为它没有等待完成并立即向`UniTaskScheduler.UnobservedTaskException`报告错误如果您不需要等待(即发即弃),那么使用`UniTaskVoid`会更好。不幸的是,要解除警告,您需要在尾部添加`Forget()`
```csharp ```csharp
public async UniTaskVoid FireAndForgetMethod() public async UniTaskVoid FireAndForgetMethod()
@@ -599,7 +620,7 @@ public void Caller()
} }
``` ```
UniTask 也有`Forget`方法,类似`UniTaskVoid`且效果相同。但是如果完全不需要使用`await``UniTaskVoid`会更高效。 UniTask 也有`Forget`方法,`UniTaskVoid`类似且效果相同。如果完全不需要使用`await`那么使用`UniTaskVoid`会更高效。
```csharp ```csharp
public async UniTask DoAsync() public async UniTask DoAsync()
@@ -614,7 +635,7 @@ public void Caller()
} }
``` ```
要使用注册到事件的异步 lambda请不要使用`async void`. 相反,您可以使用`UniTask.Action``UniTask.UnityAction`两者都通过`async UniTaskVoid` lambda 创建委托。 要使用注册到事件的异步 lambda请不要使用`async void`您可以使用`UniTask.Action``UniTask.UnityAction`来代替,这两者都通过`async UniTaskVoid` lambda 创建委托。
```csharp ```csharp
Action actEvent; Action actEvent;
@@ -636,7 +657,7 @@ class Sample : MonoBehaviour
{ {
async UniTaskVoid Start() async UniTaskVoid Start()
{ {
// async init code. // 异步初始化代码。
} }
} }
``` ```
@@ -647,22 +668,22 @@ UniTaskTracker
![image](https://user-images.githubusercontent.com/46207/83527073-4434bf00-a522-11ea-86e9-3b3975b26266.png) ![image](https://user-images.githubusercontent.com/46207/83527073-4434bf00-a522-11ea-86e9-3b3975b26266.png)
* Enable AutoReload(Toggle) - 自动重新加载。 - Enable AutoReload(Toggle) - 自动重新加载。
* Reload - 重新加载视图重新扫描内存中UniTask实例并刷新界面 - Reload - 重新加载视图重新扫描内存中UniTask实例并刷新界面
* GC.Collect - 调用 GC.Collect。 - GC.Collect - 调用 GC.Collect。
* Enable Tracking(Toggle) - 开始跟踪异步/等待 UniTask。性能影响低。 - Enable Tracking(Toggle) - 开始跟踪异步/等待 UniTask。性能影响低。
* Enable StackTrace(Toggle) - 在任务启动时捕获 StackTrace。性能影响高。 - Enable StackTrace(Toggle) - 在任务启动时捕获 StackTrace。性能影响高。
UniTaskTracker 仅用于调试用途,因为启用跟踪和捕获堆栈跟踪很有用,但会对性能产生重大影响。推荐的用法是启用跟踪和堆栈跟踪以查找任务泄漏并在完成时禁用它们。 UniTaskTracker 仅用于调试用途,因为启用跟踪和捕获堆栈跟踪很有用,但会对性能产生重大影响。推荐的用法是只在查找任务泄漏时启用跟踪和堆栈跟踪,并在使用完毕后禁用它们。
外部拓展 外部拓展
--- ---
默认情况下UniTask 支持 TextMeshPro`BindTo(TMP_Text)``TMP_InputField`,并且TMP_InputField有同原生uGUI `InputField`类似的事件扩展、DOTween`Tween`作为等待和Addressables`AsyncOperationHandle``AsyncOperationHandle<T>`作为等待)。 默认情况下UniTask 支持 TextMeshPro`BindTo(TMP_Text)`像原生 uGUI `InputField` 那样的事件扩展,如`TMP_InputField`、DOTween`Tween`作为等待)和 Addressables`AsyncOperationHandle``AsyncOperationHandle<T>`作为等待)。
在单独的 asmdef 中定义,`UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`. 它们被定义在了`UniTask.TextMeshPro``UniTask.DOTween``UniTask.Addressables`等单独的 asmdef文件中。
Package manager 中导入软件包时,会自动启用对 TextMeshPro 和 Addressables 的支持。 包管理器中导入软件包时,会自动启用对 TextMeshPro 和 Addressables 的支持。
但对于 DOTween 支持,则需要从 [DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r) 中导入并定义脚本定义符号 `UNITASK_DOTWEEN_SUPPORT` 后才能启用。 但对于 DOTween 支持,则需要从[DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r)中导入并定义脚本定义符号`UNITASK_DOTWEEN_SUPPORT`后才能启用。
```csharp ```csharp
// 动画序列 // 动画序列
@@ -677,31 +698,44 @@ await UniTask.WhenAll(
transform.DOScale(10, 3).WithCancellation(ct)); transform.DOScale(10, 3).WithCancellation(ct));
``` ```
DOTween 支持的默认行为( `await`, `WithCancellation`, `ToUniTask`) await tween 被终止。它适用于 Complete(true/false) 和 Kill(true/false)。但是如果你想重用tweens ( `SetAutoKill(false)`)它就不能按预期工作。如果您想等待另一个时间点Tween 中存在以下扩展方法,`AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete` DOTween 支持的默认行为`await``WithCancellation``ToUniTask` 会等待到 tween 被终止。它适用于 Complete(true/false) 和 Kill(true/false)。但是如果您想复用 tweens`SetAutoKill(false)`它就不能按预期工作。如果您想等待另一个时间点Tween 中存在以下扩展方法,`AwaitForComplete``AwaitForPause``AwaitForPlay``AwaitForRewind``AwaitForStepComplete`
AsyncEnumerable 和 Async LINQ AsyncEnumerable 和 Async LINQ
--- ---
Unity 2020.2 支持 C# 8.0,因此您可以使用`await foreach`. 这是异步时代的新更新符号。 Unity 2020.2 支持 C# 8.0,因此您可以使用`await foreach`这是异步时代的新更新符号。
```csharp ```csharp
// Unity 2020.2, C# 8.0 // Unity 2020.2C# 8.0
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token)) await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate().WithCancellation(token))
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
} }
``` ```
在 C# 7.3 环境中,您可以使用`ForEachAsync`方法以几乎相同的方式工作。 在 C# 7.3 环境中,您可以使用`ForEachAsync`方法以几乎相同的方式工作。
```csharp ```csharp
// C# 7.3(Unity 2018.3~) // C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ => await UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(_ =>
{ {
Debug.Log("Update() " + Time.frameCount); Debug.Log("Update() " + Time.frameCount);
}); }, token);
``` ```
UniTaskAsyncEnumerable 实现异步 LINQ类似于 LINQ 的`IEnumerable<T>`或 Rx `IObservable<T>`。所有标准 LINQ 查询运算符都可以应用于异步流。例如,以下代码表示如何将 Where 过滤器应用于每两次单击运行一次的按钮单击异步流 `UniTask.WhenEach`类似于 .NET 9`Task.WhenEach`,它可以使用新的方式来等待多个任务
```csharp
await foreach (var result in UniTask.WhenEach(task1, task2, task3))
{
// 结果的类型为 WhenEachResult<T>。
// 它包含 `T Result` or `Exception Exception`。
// 您可以检查 `IsCompletedSuccessfully` 或 `IsFaulted` 以确定是访 `.Result` 还是 `.Exception`。
// 如果希望在 `IsFaulted` 时抛出异常并在成功时获取结果,可以使用 `GetResult()`。
Debug.Log(result.GetResult());
}
```
UniTaskAsyncEnumerable 实现了异步 LINQ类似于 LINQ 的`IEnumerable<T>`或 Rx 的 `IObservable<T>`。所有标准 LINQ 查询运算符都可以应用于异步流。例如,以下代码展示了如何将 Where 过滤器应用于每两次单击运行一次的按钮点击异步流。
```csharp ```csharp
await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ => await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
@@ -709,7 +743,7 @@ await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsy
}); });
``` ```
Fire and Forget 风格(例如,事件处理),也可以使用`Subscribe`. 即发即弃(Fire and Forget风格(例如,事件处理),也可以使用`Subscribe`
```csharp ```csharp
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ => okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
@@ -717,13 +751,13 @@ okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
}); });
``` ```
Async LINQ 在 时启用`using Cysharp.Threading.Tasks.Linq;`,并且`UniTaskAsyncEnumerable``UniTask.Linq`asmdef 中定义。 在引入`using Cysharp.Threading.Tasks.Linq;`后,异步 LINQ 将被启用,并且`UniTaskAsyncEnumerable` asmdef 文件`UniTask.Linq`中定义。
它更接近 UniRxReactive Extensions但 UniTaskAsyncEnumerable 是pull-base的异步流,而 Rx 是基于push-base异步流。请注意,尽管相似,但特不同,并且细节的行为也随之不同。 它更接近 UniRxReactive Extensions但 UniTaskAsyncEnumerable 是基于 pull 的异步流,而 Rx 是基于 push异步流。请注意,尽管它们相似,但特不同,细节也有所不同。
`UniTaskAsyncEnumerable`是类似的入口点`Enumerable`。除了标准查询运算符之外,还有其他 Unity 生成器,例如`EveryUpdate``Timer``TimerFrame``Interval``IntervalFrame``EveryValueChanged`并且还添加了额外的 UniTask 原始查询运算符,如`Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe` `UniTaskAsyncEnumerable`是类似`Enumerable`的入口点。除了标准查询操作符之外,还 Unity 提供了其他生成器,例如`EveryUpdate``Timer``TimerFrame``Interval``IntervalFrame``EveryValueChanged`此外,还添加了 UniTask 原生的查询操作符,如`Append``Prepend``DistinctUntilChanged``ToHashSet``Buffer``CombineLatest``Do``Never``ForEachAsync``Pairwise``Publish``Queue``Return``SkipUntil``TakeUntil``SkipUntilCanceled``TakeUntilCanceled``TakeLast``Subscribe`
以 Func 作为参数的方法具有三个额外的重载,`***Await`, `***AwaitWithCancellation` 以 Func 作为参数的方法具有三个额外的重载,另外两个是`***Await``***AwaitWithCancellation`
```csharp ```csharp
Select(Func<T, TR> selector) Select(Func<T, TR> selector)
@@ -731,12 +765,12 @@ SelectAwait(Func<T, UniTask<TR>> selector)
SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector) SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
``` ```
如果在 func 方法内部使用`async`,请使用***Awaitor `***AwaitWithCancellation` 如果在 func 内部使用`async`方法,请使用`***Await``***AwaitWithCancellation`
如何创建异步迭代器C# 8.0 支持异步迭代器(`async yield return`),但它只允许`IAsyncEnumerable<T>`并且当然需要 C# 8.0。UniTask 支持`UniTaskAsyncEnumerable.Create`创建自定义异步迭代器的方法 如何创建异步迭代器C# 8.0 支持异步迭代器(`async yield return`),但它只允许`IAsyncEnumerable<T>`当然需要 C# 8.0。UniTask 支持使用`UniTaskAsyncEnumerable.Create`方法来创建自定义异步迭代器。
```csharp ```csharp
// IAsyncEnumerable, C# 8.0 异步迭代器. ( 不要这样用因为IAsyncEnumerable不被UniTask控制). // IAsyncEnumerableC# 8.0 异步迭代器。(请不要这样使用,因为 IAsyncEnumerable 不被 UniTask 所控制)。
public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default) public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default)
{ {
var frameCount = 0; var frameCount = 0;
@@ -751,14 +785,14 @@ public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]Cancell
// UniTaskAsyncEnumerable.Create 并用 `await writer.YieldAsync` 代替 `yield return`. // UniTaskAsyncEnumerable.Create 并用 `await writer.YieldAsync` 代替 `yield return`.
public IUniTaskAsyncEnumerable<int> MyEveryUpdate() public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
{ {
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method. // writer(IAsyncWriter<T>) `YieldAsync(value)` 方法。
return UniTaskAsyncEnumerable.Create<int>(async (writer, token) => return UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{ {
var frameCount = 0; var frameCount = 0;
await UniTask.Yield(); await UniTask.Yield();
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
await writer.YieldAsync(frameCount++); // instead of `yield return` await writer.YieldAsync(frameCount++); // 代替 `yield return`
await UniTask.Yield(); await UniTask.Yield();
} }
}); });
@@ -767,7 +801,7 @@ public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
可等待事件 可等待事件
--- ---
所有 uGUI 组件都实现`***AsAsyncEnumerable`了异步事件流的转换。 所有 uGUI 组件都实现`***AsAsyncEnumerable`,以实现对事件的异步流的转换。
```csharp ```csharp
async UniTask TripleClick() async UniTask TripleClick()
@@ -809,7 +843,7 @@ async UniTask TripleClick(CancellationToken token)
} }
``` ```
所有 MonoBehaviour 消息事件都可以转换异步流`AsyncTriggers`,可以通过`using Cysharp.Threading.Tasks.Triggers;`进行启用,.AsyncTrigger 可以使用 UniTaskAsyncEnumerable 来创建,通过`GetAsync***Trigger`触发。 所有 MonoBehaviour 消息事件均可通过`AsyncTriggers`转换异步流`AsyncTriggers`可通过引入`using Cysharp.Threading.Tasks.Triggers;`来启用。`AsyncTriggers`可以使用`GetAsync***Trigger`来创建,并将它作为 UniTaskAsyncEnumerable 来触发。
```csharp ```csharp
var trigger = this.GetOnCollisionEnterAsyncHandler(); var trigger = this.GetOnCollisionEnterAsyncHandler();
@@ -817,18 +851,18 @@ await trigger.OnCollisionEnterAsync();
await trigger.OnCollisionEnterAsync(); await trigger.OnCollisionEnterAsync();
await trigger.OnCollisionEnterAsync(); await trigger.OnCollisionEnterAsync();
// every moves. // 每次移动触发。
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData => await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
{ {
}); });
``` ```
`AsyncReactiveProperty`,`AsyncReadOnlyReactiveProperty`是 UniTask 的 ReactiveProperty 版本。将异步流值绑定到 Unity 组件Text/Selectable/TMP/Text`BindTo``IUniTaskAsyncEnumerable<T>`扩展方法。 `AsyncReactiveProperty``AsyncReadOnlyReactiveProperty`是 UniTask 的 ReactiveProperty 版本。`BindTo``IUniTaskAsyncEnumerable<T>`扩展方法,可以把异步流值绑定到 Unity 组件Text/Selectable/TMP/Text
```csharp ```csharp
var rp = new AsyncReactiveProperty<int>(99); var rp = new AsyncReactiveProperty<int>(99);
// AsyncReactiveProperty 本身是 IUniTaskAsyncEnumerable, 可以通过LINQ进行查询 // AsyncReactiveProperty 本身是 IUniTaskAsyncEnumerable可以通过 LINQ 进行查询
rp.ForEachAsync(x => rp.ForEachAsync(x =>
{ {
Debug.Log(x); Debug.Log(x);
@@ -851,16 +885,16 @@ var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyAsyncReactiveProper
在序列中的异步处理完成之前pull-based异步流不会获取下一个值。这可能会从按钮等推送类型的事件中溢出数据。 在序列中的异步处理完成之前pull-based异步流不会获取下一个值。这可能会从按钮等推送类型的事件中溢出数据。
```csharp ```csharp
// 在3s完成无法获取event // 在3s延迟结束前,无法获取 event
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x => await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
{ {
await UniTask.Delay(TimeSpan.FromSeconds(3)); await UniTask.Delay(TimeSpan.FromSeconds(3));
}); });
``` ```
很有用(防止双击),但有时没用。 (在防止双击方面)是有用的,但有时也并非都有用。
使用`Queue()`方法还将在异步处理期间对事件进行排队。 使用`Queue()`方法在异步处理期间也会对事件进行排队。
```csharp ```csharp
// 异步处理中对 message 进行排队 // 异步处理中对 message 进行排队
@@ -870,7 +904,7 @@ await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync(async x =>
}); });
``` ```
或使用`Subscribe`, fire and forget 风格 或使用即发即弃风格的`Subscribe`
```csharp ```csharp
button.OnClickAsAsyncEnumerable().Subscribe(async x => button.OnClickAsAsyncEnumerable().Subscribe(async x =>
@@ -883,11 +917,11 @@ Channel
--- ---
`Channel`与[System.Threading.Tasks.Channels](https://docs.microsoft.com/en-us/dotnet/api/system.threading.channels?view=netcore-3.1)相同,类似于 GoLang Channel。 `Channel`与[System.Threading.Tasks.Channels](https://docs.microsoft.com/en-us/dotnet/api/system.threading.channels?view=netcore-3.1)相同,类似于 GoLang Channel。
目前只支持多生产者、单消费者无界渠道。它可以`Channel.CreateSingleConsumerUnbounded<T>()`. 目前只支持多生产者、单消费者无界 Channel。它可以通过`Channel.CreateSingleConsumerUnbounded<T>()`来创建。
对于 producer(`.Writer`),用`TryWrite`推送值`TryComplete`完成通道。对于 consumer(`.Reader`),使用`TryRead``WaitToReadAsync``ReadAsync``Completion``ReadAllAsync`来读取队列的消息。 对于生产者(`.Writer`)使`TryWrite`推送值,使用`TryComplete`完成 Channel。对于消费者(`.Reader`),使用`TryRead``WaitToReadAsync``ReadAsync``Completion``ReadAllAsync`来读取队列的消息。
`ReadAllAsync`返回`IUniTaskAsyncEnumerable<T>` 查询 LINQ 运算符。Reader 只允许单消费者,但使用`.Publish()`查询运算符来启用多播消息。例如,制作 pub/sub 实用程序 `ReadAllAsync`返回`IUniTaskAsyncEnumerable<T>` 因此可以使用 LINQ 操作符。Reader 只允许单消费者,但可以使用`.Publish()`查询操作符来启用多播消息。例如,可以制作发布/订阅工具
```csharp ```csharp
public class AsyncMessageBroker<T> : IDisposable public class AsyncMessageBroker<T> : IDisposable
@@ -922,6 +956,14 @@ public class AsyncMessageBroker<T> : IDisposable
} }
``` ```
与 Awaitable 对比
---
Unity 6 引入了可等待类型[Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html)。简而言之Awaitable 可以被认为是 UniTask 的一个子集并且事实上Awaitable的设计也受 UniTask 的影响。它应该能够处理基于 PlayerLoop 的 await池化 Task以及支持以类似的方式使用`CancellationToken`进行取消。随着它被包含在标准库中,您可能想知道是继续使用 UniTask 还是迁移到 Awaitable。以下是简要指南。
首先Awaitable 提供的功能与协程提供的功能相同。使用 await 代替`yield return``await NextFrameAsync()`代替`yield return null``WaitForSeconds``EndOfFrame`等价。然而,这只是两者之间的差异。就功能而言,它是基于协程的,缺乏基于 Task 的特性。在使用 async/await 的实际应用程序开发中,像`WhenAll`这样的操作是必不可少的。此外UniTask 支持许多基于帧的操作(如`DelayFrame`)和更灵活的 PlayerLoopTiming 控制,这些在 Awaitable 中是不可用的。当然,它也没有跟踪器窗口。
因此,我推荐在应用程序开发中使用 UniTask。UniTask 是 Awaitable 的超集,并包含了许多基本特性。对于库开发,如果您希望避免外部依赖,可以使用 Awaitable 作为方法的返回类型。因为 Awaitable 可以使用`AsUniTask`转换为 UniTask所以支持在 UniTask 库中处理基于 Awaitable 的功能。即便是在库开发中,如果您不需要担心依赖关系,使用 UniTask 也会是您的最佳选择。
单元测试 单元测试
--- ---
Unity 的`[UnityTest]`属性可以测试协程IEnumerator但不能测试异步。`UniTask.ToCoroutine`将 async/await 桥接到协程,以便您可以测试异步方法。 Unity 的`[UnityTest]`属性可以测试协程IEnumerator但不能测试异步。`UniTask.ToCoroutine`将 async/await 桥接到协程,以便您可以测试异步方法。
@@ -947,46 +989,46 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
}); });
``` ```
UniTask 自的单元测试是使用 Unity Test Runner 和[Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit)编写的,以 CI 集成并检查 IL2CPP 是否正常工作。 UniTask 自的单元测试是使用 Unity Test Runner 和[Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit)编写的,以集成到 CI 并检查 IL2CPP 是否正常工作。
## 线程池限制 ## 线程池限制
大多数 UniTask 方法在单个线程 (PlayerLoop) 上运行,只有`UniTask.Run``Task.Run`等效)和`UniTask.SwitchToThreadPool`在线程池上运行。如果您使用线程池,它将无法与 WebGL 等平台兼容。 大多数 UniTask 方法在单个线程 (PlayerLoop) 上运行,只有`UniTask.Run`等同于`Task.Run`)和`UniTask.SwitchToThreadPool`在线程池上运行。如果您使用线程池,它将无法与 WebGL 等平台兼容。
`UniTask.Run`现在已弃用。可以改用`UniTask.RunOnThreadPool`。并且还要考虑是否可以使用`UniTask.Create``UniTask.Void` `UniTask.Run`现在已弃用。可以改用`UniTask.RunOnThreadPool`。并且还要考虑是否可以使用`UniTask.Create``UniTask.Void`
## IEnumerator.ToUniTask 限制 ## IEnumerator.ToUniTask 限制
您可以将协程IEnumerator转换为 UniTask或直接等待),但它有一些限制。 您可以将协程IEnumerator转换为 UniTask或直接 await),但它有一些限制。
- 不支持`WaitForEndOfFrame``WaitForFixedUpdate``Coroutine` - 不支持`WaitForEndOfFrame``WaitForFixedUpdate``Coroutine`
- Loop生命周期与`StartCoroutine`不一样,它使用指定`PlayerLoopTiming`并且默认情况下,`PlayerLoopTiming.Update`在 MonoBehaviour`Update``StartCoroutine`的循环之前行。 - 生命周期与`StartCoroutine`不一样,它使用指定`PlayerLoopTiming`并且默认情况下,`PlayerLoopTiming.Update`在 MonoBehaviour`Update``StartCoroutine`的循环之前行。
如果您想要从协程到异步的完全兼容转换,请使用`IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)`重载。它在参数 MonoBehaviour 实例执行 StartCoroutine 并等待它在 UniTask 中完成。 如果您想要实现从协程到异步的完全兼容转换,请使用`IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)`重载。它会在传入的 MonoBehaviour 实例执行 StartCoroutine 并在 UniTask 中等待它完成。
## 关于 UnityEditor ## 关于 UnityEditor
UniTask 可以像编辑器协程一样在 Unity 编辑器上运行。但是,有一些限制。 UniTask 可以像编辑器协程一样在 Unity 编辑器上运行。但有一些限制。
- UniTask.Delay 的 DelayType.DeltaTime、UnscaledDeltaTime 无法正常工作,因为它们无法在编辑器中获取 deltaTime。因此在 EditMode 运行,会自动将 DelayType 更改为`DelayType.Realtime`等待正确的时间 - UniTask.Delay 的 DelayType.DeltaTime、UnscaledDeltaTime 无法正常工作,因为它们无法在编辑器中获取 deltaTime。因此在 EditMode 运行,会自动将 DelayType 更改为能等待正确的时间的`DelayType.Realtime`
- 所有 PlayerLoopTiming 都在`EditorApplication.update`生命周期上运行。 - 所有 PlayerLoopTiming 都在`EditorApplication.update`生命周期上运行。
-`-quit``-batchmode`不起作用,因为 Unity`EditorApplication.update`在单帧后不会运行并退出。相反,不要使用`-quit`手动退出`EditorApplication.Exit(0)`. -`-quit``-batchmode`不起作用,因为 Unity 不会执行 `EditorApplication.update` 并在一帧后退出。因此,不要使用`-quit`使用`EditorApplication.Exit(0)`手动退出。
与原生 Task API 对比 与原生 Task API 对比
--- ---
UniTask 有许多原生的 Task-like API。此表示了一一对应的 API 是什么 UniTask 有许多原生的Task API。此表示了两者相对应的 API。
使用原生类型。 使用原生类型。
| .NET Type | UniTask Type | | .NET 类型 | UniTask 类型 |
| --- | --- | |---------------------------| --- |
| `IProgress<T>` | --- | | `IProgress<T>` | --- |
| `CancellationToken` | --- | | `CancellationToken` | --- |
| `CancellationTokenSource` | --- | | `CancellationTokenSource` | --- |
使用 UniTask 类型. 使用 UniTask 类型
| .NET Type | UniTask Type | | .NET 类型 | UniTask 类型 |
| --- | --- | | --- | --- |
| `Task`/`ValueTask` | `UniTask` | | `Task`/`ValueTask` | `UniTask` |
| `Task<T>`/`ValueTask<T>` | `UniTask<T>` | | `Task<T>`/`ValueTask<T>` | `UniTask<T>` |
@@ -1011,6 +1053,7 @@ UniTask 有许多原生的 Task-like API。此表显示了一一对应的 API
| `Task.Run` | `UniTask.RunOnThreadPool` | | `Task.Run` | `UniTask.RunOnThreadPool` |
| `Task.WhenAll` | `UniTask.WhenAll` | | `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` | | `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.WhenEach` | `UniTask.WhenEach` |
| `Task.CompletedTask` | `UniTask.CompletedTask` | | `Task.CompletedTask` | `UniTask.CompletedTask` |
| `Task.FromException` | `UniTask.FromException` | | `Task.FromException` | `UniTask.FromException` |
| `Task.FromResult` | `UniTask.FromResult` | | `Task.FromResult` | `UniTask.FromResult` |
@@ -1020,7 +1063,7 @@ UniTask 有许多原生的 Task-like API。此表显示了一一对应的 API
池化配置 池化配置
--- ---
UniTask 积极缓存异步promise对象实现零分配(有关技术细节,请参阅博客文章[UniTask v2 — Unity 的零分配异步/等待,使用异步 LINQ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)。默认情况下它缓存所有promise ,但您可以配置`TaskPool.SetMaxPoolSize`为您的值,该值表示每种类型的缓存大小。`TaskPool.GetCacheSizeInfo`返回池中当前缓存的对象。 UniTask 通过积极缓存异步 promise 对象实现零堆内存分配(有关技术细节,请参阅博客文章[UniTask v2 — 适用于 Unity 的零堆内存分配的async/await支持异步 LINQ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd))。默认情况下,它缓存所有 promise但您可以通过调用`TaskPool.SetMaxPoolSize`方法来自定义每种类型的最大缓存大小。`TaskPool.GetCacheSizeInfo`返回池中当前缓存的对象。
```csharp ```csharp
foreach (var (type, size) in TaskPool.GetCacheSizeInfo()) foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
@@ -1029,19 +1072,19 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
} }
``` ```
Profiler下的分配 Profiler 下的堆内存分配
--- ---
在 UnityEditor 中,分析器显示编译器生成的 AsyncStateMachine 的分配,但它只发生在调试(开发构建中。C# 编译器将 AsyncStateMachine 生成为 Debug 构建的类和 Release 构建结构。 在 UnityEditor 中,能从 profiler 中看到编译器生成的 AsyncStateMachine 的堆内存分配,但它只出现在Debugdevelopment构建中。C# 编译器在Debug 构建时将 AsyncStateMachine 生成为类,而在Release 构建时将其生成为结构。
Unity 从 2020.1 开始支持代码优化选项(右,页脚)。 Unity 从2020.1版本开始支持代码优化选项(位于右下角)。
![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png) ![](https://user-images.githubusercontent.com/46207/89967342-2f944600-dc8c-11ea-99fc-0b74527a16f6.png)
您可以将 C# 编译器优化更改为 release 以删除开发版本中的 AsyncStateMachine 分配。此优化选项也可以通过设置`Compilation.CompilationPipeline-codeOptimization``Compilation.CodeOptimization` 在开发构建中,您可以通过将 C# 编译器优化设置为 release 模式来移除 AsyncStateMachine 的堆内存分配。此优化选项也可以通过`Compilation.CompilationPipeline-codeOptimization``Compilation.CodeOptimization`来设置
UniTaskSynchronizationContext UniTaskSynchronizationContext
--- ---
Unity 的默认 SynchronizationContext( `UnitySynchronizationContext`) 在性能方面表现不佳。UniTask 绕过`SynchronizationContext`(和`ExecutionContext`) 因此不使用它,但如果存在`async Task`,则仍然使用它。`UniTaskSynchronizationContext``UnitySynchronizationContext`性能更好的替代品。 Unity 的默认 SynchronizationContext(`UnitySynchronizationContext`) 在性能方面表现不佳。UniTask 绕过`SynchronizationContext`(和`ExecutionContext`) 因此 UniTask 不使用它,但如果存在`async Task`,则仍然使用它。`UniTaskSynchronizationContext``UnitySynchronizationContext`性能更好的替代品。
```csharp ```csharp
public class SyncContextInjecter public class SyncContextInjecter
@@ -1056,45 +1099,38 @@ public class SyncContextInjecter
这是一个可选的选择,并不总是推荐;`UniTaskSynchronizationContext`性能不如`async UniTask`,并且不是完整的 UniTask 替代品。它也不保证与`UnitySynchronizationContext`完全兼容 这是一个可选的选择,并不总是推荐;`UniTaskSynchronizationContext`性能不如`async UniTask`,并且不是完整的 UniTask 替代品。它也不保证与`UnitySynchronizationContext`完全兼容
API References API 文档
--- ---
UniTask 的 API 参考由[DocFX](https://dotnet.github.io/docfx/)和[Cysharp/DocfXTemplate托管在](https://github.com/Cysharp/DocfxTemplate)[cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html)上 UniTask 的 API 文档托管在[cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html),使用[DocFX](https://dotnet.github.io/docfx/)和[Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate)生成
例如UniTask 的工厂方法可以在[UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1)中看到。UniTaskAsyncEnumerable 的工厂/扩展方法可以在[UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1)中看到 例如UniTask 的工厂方法可以在[UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1)中查阅。UniTaskAsyncEnumerable 的工厂方法和扩展方法可以在[UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1)中查阅
UPM Package UPM
--- ---
### 通过 git URL 安装 ### 通过 git URL 安装
需要支持 git 包路径查询参数的 unity 版本Unity >= 2019.3.4f1Unity >= 2020.1a21)。您可以添加`https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask`到包管理器 需要支持 git 包路径查询参数的 Unity 版本Unity >= 2019.3.4f1Unity >= 2020.1a21)。您可以在包管理器中添加`https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask`
![image](https://user-images.githubusercontent.com/46207/79450714-3aadd100-8020-11ea-8aae-b8d87fc4d7be.png) ![image](https://user-images.githubusercontent.com/46207/79450714-3aadd100-8020-11ea-8aae-b8d87fc4d7be.png)
![image](https://user-images.githubusercontent.com/46207/83702872-e0f17c80-a648-11ea-8183-7469dcd4f810.png) ![image](https://user-images.githubusercontent.com/46207/83702872-e0f17c80-a648-11ea-8183-7469dcd4f810.png)
或添加`"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"``Packages/manifest.json`. `Packages/manifest.json`添加`"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"`
如果要设置目标版本,UniTask 使用`*.*.*`发布标签,因此您可以指定一个版本,如`#2.1.0`. 例如`https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`. UniTask 使用`*.*.*`发布标签来指定版本,因此如果您要设置指定版本,您可以在后面添加像`#2.1.0`这样的版本标签。例如`https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`
### 通过 OpenUPM 安装
该软件包在[openupm 注册表](https://openupm.com/)中可用。建议通过[openupm-cli](https://github.com/openupm/openupm-cli)安装。 关于 .NET Core
```
openupm add com.cysharp.unitask
```
.NET Core
--- ---
对于 .NET Core请使用 NuGet。 对于 .NET Core请使用 NuGet。
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask) > PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
.NET Core 版本的 UniTask 是 Unity UniTask 的子集,移除了 PlayerLoop 依赖的方法。 .NET Core 版本的 UniTask 是 Unity 版本的 UniTask 的子集,移除了依赖 PlayerLoop 的方法。
它以比标准 Task/ValueTask 更高的性能运行,但在使用时应注意忽略 ExecutionContext/SynchronizationContext。`AsyncLocal`也不起作用,因为它忽略了 ExecutionContext。 相比于原生 TaskValueTask,它能以更高的性能运行,但在使用时应注意忽略 ExecutionContextSynchronizationContext。因为它忽略了 ExecutionContext`AsyncLocal`也不起作用
如果您在内部使用 UniTask但将 ValueTask 作为外部 API 提供,您可以编写如下(受[PooledAwait](https://github.com/mgravell/PooledAwait)启发)代码 如果您在内部使用 UniTask但将 ValueTask 作为外部 API 提供,您可以编写如下代码(受[PooledAwait](https://github.com/mgravell/PooledAwait)启发)。
```csharp ```csharp
public class ZeroAllocAsyncAwaitInDotNetCore public class ZeroAllocAsyncAwaitInDotNetCore
@@ -1125,10 +1161,10 @@ public ValueTask TestAsync()
} }
``` ```
.NET Core 版本允许用户在与Unity共享代码时例如[CysharpOnion](https://github.com/Cysharp/MagicOnion/)像使用接口一样使用UniTask。.NET Core 版本的 UniTask 可以提供丝滑的代码共享体验 .NET Core 版本的 UniTask 是为了让用户在与 Unity 共享代码时(例如使用[CysharpOnion](https://github.com/Cysharp/MagicOnion/)能够将 UniTask 用作接口。.NET Core 版本的 UniTask 使得代码共享更加顺畅
WhenAll 等实用方法作为 UniTask 的补充,由[Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement)提供。 [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement)提供了一些实用方法,如 WhenAll这些方法等效于 UniTask
License 许可证
--- ---
仓库基于MIT协议 库采用MIT许可证

View File

@@ -4,7 +4,11 @@
"src": [ "src": [
{ {
"files": [ "files": [
"UniTask/Assets/Plugins/UniTask/Runtime/**/*.cs" "UniTask/Library/ScriptAssemblies/UniTask*.dll"
],
"exclude": [
"UniTask/Library/ScriptAssemblies/UniTask.Tests.dll",
"UniTask/Library/ScriptAssemblies/UniTask.Tests.Editor.dll"
], ],
"src": "../src" "src": "../src"
} }
@@ -54,7 +58,6 @@
} }
], ],
"dest": "_site", "dest": "_site",
"globalMetadataFiles": [], "globalMetadataFiles": [],
"fileMetadataFiles": [], "fileMetadataFiles": [],
"template": [ "template": [

View File

@@ -5,31 +5,16 @@
<AssemblyName>UniTask</AssemblyName> <AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace> <RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
<DefineConstants>UNITASK_NETCORE</DefineConstants>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn> <NoWarn>$(NoWarn);CS1591</NoWarn>
<!-- NuGet Packaging --> <!-- NuGet Packaging -->
<Id>UniTask</Id>
<PackageVersion>$(Version)</PackageVersion>
<Company>Cysharp</Company>
<Authors>Cysharp</Authors>
<Copyright>© Cysharp, Inc.</Copyright>
<PackageTags>task;async</PackageTags>
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<Id>UniTask</Id>
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Include="Icon.png" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" Exclude="&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopTimer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;&#xD;&#xA;" /> <Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" Exclude="&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;&#xD;&#xA; &#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopTimer.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;&#xD;&#xA;..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;&#xD;&#xA;" />
<Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" /> <Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />

View File

@@ -4,7 +4,6 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<RootNamespace>NetCoreSandbox</RootNamespace> <RootNamespace>NetCoreSandbox</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -16,8 +15,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" /> <ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj"> <ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType> <OutputItemType>Analyzer</OutputItemType>

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1998
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1998
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;

View File

@@ -2,9 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>NetCoreTests</RootNamespace> <RootNamespace>NetCoreTests</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@@ -0,0 +1,69 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class WhenEachTest
{
[Fact]
public async Task Each()
{
var a = Delay(1, 3000);
var b = Delay(2, 1000);
var c = Delay(3, 2000);
var l = new List<int>();
await foreach (var item in UniTask.WhenEach(a, b, c))
{
l.Add(item.Result);
}
l.Should().Equal(2, 3, 1);
}
[Fact]
public async Task Error()
{
var a = Delay2(1, 3000);
var b = Delay2(2, 1000);
var c = Delay2(3, 2000);
var l = new List<WhenEachResult<int>>();
await foreach (var item in UniTask.WhenEach(a, b, c))
{
l.Add(item);
}
l[0].IsCompletedSuccessfully.Should().BeTrue();
l[0].IsFaulted.Should().BeFalse();
l[0].Result.Should().Be(2);
l[1].IsCompletedSuccessfully.Should().BeFalse();
l[1].IsFaulted.Should().BeTrue();
l[1].Exception.Message.Should().Be("ERROR");
l[2].IsCompletedSuccessfully.Should().BeTrue();
l[2].IsFaulted.Should().BeFalse();
l[2].Result.Should().Be(1);
}
async UniTask<int> Delay(int id, int sleep)
{
await Task.Delay(sleep);
return id;
}
async UniTask<int> Delay2(int id, int sleep)
{
await Task.Delay(sleep);
if (id == 3) throw new Exception("ERROR");
return id;
}
}
}

View File

@@ -7,6 +7,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Threading; using System.Threading;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.AsyncOperations;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
@@ -20,12 +21,12 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(handle).GetAwaiter(); return ToUniTask(handle).GetAwaiter();
} }
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken) public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
return ToUniTask(handle, cancellationToken: cancellationToken); return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
} }
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
@@ -44,7 +45,7 @@ namespace Cysharp.Threading.Tasks
return UniTask.CompletedTask; return UniTask.CompletedTask;
} }
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token); return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
} }
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
@@ -103,20 +104,23 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
} }
readonly Action<AsyncOperationHandle> continuationAction; readonly Action<AsyncOperationHandle> completedCallback;
AsyncOperationHandle handle; AsyncOperationHandle handle;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
IProgress<float> progress; IProgress<float> progress;
bool autoReleaseWhenCanceled;
bool cancelImmediately;
bool completed; bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleConfiguredSource() AsyncOperationHandleConfiguredSource()
{ {
continuationAction = Continuation; completedCallback = HandleCompleted;
} }
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -131,31 +135,52 @@ namespace Cysharp.Threading.Tasks
result.handle = handle; result.handle = handle;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
result.completed = false; result.completed = false;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (AsyncOperationHandleConfiguredSource)state;
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
{
Addressables.Release(promise.handle);
}
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result); PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction; handle.Completed += result.completedCallback;
token = result.core.Version; token = result.core.Version;
return result; return result;
} }
void Continuation(AsyncOperationHandle _) void HandleCompleted(AsyncOperationHandle _)
{ {
handle.Completed -= continuationAction; if (handle.IsValid())
{
handle.Completed -= completedCallback;
}
if (completed) if (completed)
{ {
TryReturn(); return;
} }
else
{
completed = true; completed = true;
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
} }
else if (handle.Status == AsyncOperationStatus.Failed) else if (handle.Status == AsyncOperationStatus.Failed)
@@ -167,12 +192,25 @@ namespace Cysharp.Threading.Tasks
core.TrySetResult(AsyncUnit.Default); core.TrySetResult(AsyncUnit.Default);
} }
} }
}
public void GetResult(short token) public void GetResult(short token)
{
try
{ {
core.GetResult(token); core.GetResult(token);
} }
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
@@ -193,20 +231,23 @@ namespace Cysharp.Threading.Tasks
{ {
if (completed) if (completed)
{ {
TryReturn();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
completed = true; completed = true;
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
if (progress != null && handle.IsValid()) if (progress != null && handle.IsValid())
{ {
progress.Report(handle.PercentComplete); progress.Report(handle.GetDownloadStatus().Percent);
} }
return true; return true;
@@ -219,6 +260,7 @@ namespace Cysharp.Threading.Tasks
handle = default; handle = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -232,12 +274,12 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(handle).GetAwaiter(); return ToUniTask(handle).GetAwaiter();
} }
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken) public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
return ToUniTask(handle, cancellationToken: cancellationToken); return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
} }
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
{ {
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
@@ -255,7 +297,7 @@ namespace Cysharp.Threading.Tasks
return UniTask.FromResult(handle.Result); return UniTask.FromResult(handle.Result);
} }
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token); return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
} }
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>> sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
@@ -269,20 +311,23 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
} }
readonly Action<AsyncOperationHandle<T>> continuationAction; readonly Action<AsyncOperationHandle<T>> completedCallback;
AsyncOperationHandle<T> handle; AsyncOperationHandle<T> handle;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
IProgress<float> progress; IProgress<float> progress;
bool autoReleaseWhenCanceled;
bool cancelImmediately;
bool completed; bool completed;
UniTaskCompletionSourceCore<T> core; UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleConfiguredSource() AsyncOperationHandleConfiguredSource()
{ {
continuationAction = Continuation; completedCallback = HandleCompleted;
} }
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -298,30 +343,50 @@ namespace Cysharp.Threading.Tasks
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.completed = false; result.completed = false;
result.progress = progress; result.progress = progress;
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (AsyncOperationHandleConfiguredSource<T>)state;
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
{
Addressables.Release(promise.handle);
}
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result); PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction; handle.Completed += result.completedCallback;
token = result.core.Version; token = result.core.Version;
return result; return result;
} }
void Continuation(AsyncOperationHandle<T> argHandle) void HandleCompleted(AsyncOperationHandle<T> argHandle)
{ {
handle.Completed -= continuationAction; if (handle.IsValid())
{
handle.Completed -= completedCallback;
}
if (completed) if (completed)
{ {
TryReturn(); return;
} }
else
{
completed = true; completed = true;
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
} }
else if (argHandle.Status == AsyncOperationStatus.Failed) else if (argHandle.Status == AsyncOperationStatus.Failed)
@@ -333,12 +398,25 @@ namespace Cysharp.Threading.Tasks
core.TrySetResult(argHandle.Result); core.TrySetResult(argHandle.Result);
} }
} }
}
public T GetResult(short token) public T GetResult(short token)
{
try
{ {
return core.GetResult(token); return core.GetResult(token);
} }
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
{ {
@@ -364,20 +442,23 @@ namespace Cysharp.Threading.Tasks
{ {
if (completed) if (completed)
{ {
TryReturn();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
completed = true; completed = true;
if (autoReleaseWhenCanceled && handle.IsValid())
{
Addressables.Release(handle);
}
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
if (progress != null && handle.IsValid()) if (progress != null && handle.IsValid())
{ {
progress.Report(handle.PercentComplete); progress.Report(handle.GetDownloadStatus().Percent);
} }
return true; return true;
@@ -390,6 +471,7 @@ namespace Cysharp.Threading.Tasks
handle = default; handle = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@@ -2,7 +2,8 @@
"name": "UniTask.Addressables", "name": "UniTask.Addressables",
"references": [ "references": [
"UniTask", "UniTask",
"Unity.ResourceManager" "Unity.ResourceManager",
"Unity.Addressables"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@@ -16,6 +16,11 @@
"name": "com.unity.textmeshpro", "name": "com.unity.textmeshpro",
"expression": "", "expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT" "define": "UNITASK_TEXTMESHPRO_SUPPORT"
},
{
"name": "com.unity.ugui",
"expression": "2.0.0",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
} }
], ],
"noEngineReferences": false "noEngineReferences": false

View File

@@ -1,4 +1,9 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
#pragma warning disable CS0108
#if (UNITASK_NETCORE && !NETSTANDARD2_0) || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -19,9 +24,8 @@ namespace Cysharp.Threading.Tasks
// similar as IValueTaskSource // similar as IValueTaskSource
public interface IUniTaskSource public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
: System.Threading.Tasks.Sources.IValueTaskSource : System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif #endif
{ {
UniTaskStatus GetStatus(short token); UniTaskStatus GetStatus(short token);
@@ -30,8 +34,7 @@ namespace Cysharp.Threading.Tasks
UniTaskStatus UnsafeGetStatus(); // only for debug use. UniTaskStatus UnsafeGetStatus(); // only for debug use.
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
#pragma warning restore CS0108
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token) System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
{ {
@@ -53,13 +56,13 @@ namespace Cysharp.Threading.Tasks
} }
public interface IUniTaskSource<out T> : IUniTaskSource public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
, System.Threading.Tasks.Sources.IValueTaskSource<T> , System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif #endif
{ {
new T GetResult(short token); new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0 #if SUPPORT_VALUETASK
new public UniTaskStatus GetStatus(short token) new public UniTaskStatus GetStatus(short token)
{ {

View File

@@ -27,7 +27,9 @@ namespace Cysharp.Threading.Tasks.Linq
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources) public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources)
{ {
return new Merge<T>(sources.ToArray()); return sources is IUniTaskAsyncEnumerable<T>[] array
? new Merge<T>(array)
: new Merge<T>(sources.ToArray());
} }
public static IUniTaskAsyncEnumerable<T> Merge<T>(params IUniTaskAsyncEnumerable<T>[] sources) public static IUniTaskAsyncEnumerable<T> Merge<T>(params IUniTaskAsyncEnumerable<T>[] sources)

View File

@@ -4,38 +4,50 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
return new EveryUpdate(updateTiming); return new EveryUpdate(updateTiming, cancelImmediately);
} }
} }
internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit> internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit>
{ {
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly bool cancelImmediately;
public EveryUpdate(PlayerLoopTiming updateTiming) public EveryUpdate(PlayerLoopTiming updateTiming, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryUpdate(updateTiming, cancellationToken); return new _EveryUpdate(updateTiming, cancellationToken, cancelImmediately);
} }
class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
{ {
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool disposed; bool disposed;
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken) public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryUpdate)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@@ -44,10 +56,14 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
completionSource.Reset(); completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@@ -55,6 +71,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@@ -63,7 +80,13 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;

View File

@@ -7,7 +7,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null) public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null, bool cancelImmediately = false)
where TTarget : class where TTarget : class
{ {
var unityObject = target as UnityEngine.Object; var unityObject = target as UnityEngine.Object;
@@ -15,11 +15,11 @@ namespace Cysharp.Threading.Tasks.Linq
if (isUnityObject) if (isUnityObject)
{ {
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming); return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
} }
else else
{ {
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming); return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
} }
} }
} }
@@ -30,18 +30,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly PlayerLoopTiming monitorTiming; readonly PlayerLoopTiming monitorTiming;
readonly bool cancelImmediately;
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming) public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.monitorTiming = monitorTiming; this.monitorTiming = monitorTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken); return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
} }
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
@@ -50,13 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
readonly UnityEngine.Object targetAsUnityObject; readonly UnityEngine.Object targetAsUnityObject;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool first; bool first;
TProperty currentValue; TProperty currentValue;
bool disposed; bool disposed;
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken) public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.targetAsUnityObject = target as UnityEngine.Object; this.targetAsUnityObject = target as UnityEngine.Object;
@@ -64,6 +67,16 @@ namespace Cysharp.Threading.Tasks.Linq
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.first = true; this.first = true;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryValueChanged)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(monitorTiming, this); PlayerLoopHelper.AddAction(monitorTiming, this);
} }
@@ -72,8 +85,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return new UniTask<bool>(this, completionSource.Version);
}
if (first) if (first)
{ {
@@ -86,7 +106,6 @@ namespace Cysharp.Threading.Tasks.Linq
return CompletedTasks.True; return CompletedTasks.True;
} }
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@@ -94,6 +113,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@@ -102,13 +122,18 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel. if (disposed || targetAsUnityObject == null)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
DisposeAsync().Forget(); DisposeAsync().Forget();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
TProperty nextValue = default(TProperty); TProperty nextValue = default(TProperty);
try try
{ {
@@ -139,18 +164,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly PlayerLoopTiming monitorTiming; readonly PlayerLoopTiming monitorTiming;
readonly bool cancelImmediately;
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming) public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
{ {
this.target = new WeakReference<TTarget>(target, false); this.target = new WeakReference<TTarget>(target, false);
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.monitorTiming = monitorTiming; this.monitorTiming = monitorTiming;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken); return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
} }
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
@@ -158,19 +185,30 @@ namespace Cysharp.Threading.Tasks.Linq
readonly WeakReference<TTarget> target; readonly WeakReference<TTarget> target;
readonly IEqualityComparer<TProperty> equalityComparer; readonly IEqualityComparer<TProperty> equalityComparer;
readonly Func<TTarget, TProperty> propertySelector; readonly Func<TTarget, TProperty> propertySelector;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
bool first; bool first;
TProperty currentValue; TProperty currentValue;
bool disposed; bool disposed;
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken) public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.target = target; this.target = target;
this.propertySelector = propertySelector; this.propertySelector = propertySelector;
this.equalityComparer = equalityComparer; this.equalityComparer = equalityComparer;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.first = true; this.first = true;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_EveryValueChanged)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(monitorTiming, this); PlayerLoopHelper.AddAction(monitorTiming, this);
} }
@@ -179,7 +217,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; if (disposed) return CompletedTasks.False;
completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return new UniTask<bool>(this, completionSource.Version);
}
if (first) if (first)
{ {
@@ -192,7 +238,6 @@ namespace Cysharp.Threading.Tasks.Linq
return CompletedTasks.True; return CompletedTasks.True;
} }
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@@ -200,6 +245,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@@ -208,13 +254,19 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t)) if (disposed || !target.TryGetTarget(out var t))
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
DisposeAsync().Forget(); DisposeAsync().Forget();
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
TProperty nextValue = default(TProperty); TProperty nextValue = default(TProperty);
try try
{ {

View File

@@ -6,32 +6,32 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
public static partial class UniTaskAsyncEnumerable public static partial class UniTaskAsyncEnumerable
{ {
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(dueTime, null, updateTiming, ignoreTimeScale); return new Timer(dueTime, null, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(dueTime, period, updateTiming, ignoreTimeScale); return new Timer(dueTime, period, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
{ {
return new Timer(period, period, updateTiming, ignoreTimeScale); return new Timer(period, period, updateTiming, ignoreTimeScale, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (dueTimeFrameCount < 0) if (dueTimeFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
} }
return new TimerFrame(dueTimeFrameCount, null, updateTiming); return new TimerFrame(dueTimeFrameCount, null, updateTiming, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (dueTimeFrameCount < 0) if (dueTimeFrameCount < 0)
{ {
@@ -42,16 +42,16 @@ namespace Cysharp.Threading.Tasks.Linq
throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount);
} }
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming); return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancelImmediately);
} }
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
{ {
if (intervalFrameCount < 0) if (intervalFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount);
} }
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming); return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming, cancelImmediately);
} }
} }
@@ -61,18 +61,20 @@ namespace Cysharp.Threading.Tasks.Linq
readonly TimeSpan dueTime; readonly TimeSpan dueTime;
readonly TimeSpan? period; readonly TimeSpan? period;
readonly bool ignoreTimeScale; readonly bool ignoreTimeScale;
readonly bool cancelImmediately;
public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale) public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.dueTime = dueTime; this.dueTime = dueTime;
this.period = period; this.period = period;
this.ignoreTimeScale = ignoreTimeScale; this.ignoreTimeScale = ignoreTimeScale;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken); return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken, cancelImmediately);
} }
class _Timer : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _Timer : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
@@ -81,7 +83,8 @@ namespace Cysharp.Threading.Tasks.Linq
readonly float? period; readonly float? period;
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly bool ignoreTimeScale; readonly bool ignoreTimeScale;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
int initialFrame; int initialFrame;
float elapsed; float elapsed;
@@ -89,7 +92,7 @@ namespace Cysharp.Threading.Tasks.Linq
bool completed; bool completed;
bool disposed; bool disposed;
public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken) public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken, bool cancelImmediately)
{ {
this.dueTime = (float)dueTime.TotalSeconds; this.dueTime = (float)dueTime.TotalSeconds;
this.period = (period == null) ? null : (float?)period.Value.TotalSeconds; this.period = (period == null) ? null : (float?)period.Value.TotalSeconds;
@@ -105,6 +108,16 @@ namespace Cysharp.Threading.Tasks.Linq
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.ignoreTimeScale = ignoreTimeScale; this.ignoreTimeScale = ignoreTimeScale;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_Timer)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@@ -114,12 +127,16 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw // return false instead of throw
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False; if (disposed || completed) return CompletedTasks.False;
// reset value here. // reset value here.
this.elapsed = 0; this.elapsed = 0;
completionSource.Reset(); completionSource.Reset();
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@@ -127,6 +144,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@@ -135,11 +153,16 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;
} }
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (dueTimePhase) if (dueTimePhase)
{ {
@@ -187,24 +210,27 @@ namespace Cysharp.Threading.Tasks.Linq
readonly PlayerLoopTiming updateTiming; readonly PlayerLoopTiming updateTiming;
readonly int dueTimeFrameCount; readonly int dueTimeFrameCount;
readonly int? periodFrameCount; readonly int? periodFrameCount;
readonly bool cancelImmediately;
public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming) public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, bool cancelImmediately)
{ {
this.updateTiming = updateTiming; this.updateTiming = updateTiming;
this.dueTimeFrameCount = dueTimeFrameCount; this.dueTimeFrameCount = dueTimeFrameCount;
this.periodFrameCount = periodFrameCount; this.periodFrameCount = periodFrameCount;
this.cancelImmediately = cancelImmediately;
} }
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default) public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{ {
return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken); return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken, cancelImmediately);
} }
class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
{ {
readonly int dueTimeFrameCount; readonly int dueTimeFrameCount;
readonly int? periodFrameCount; readonly int? periodFrameCount;
CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
int initialFrame; int initialFrame;
int currentFrame; int currentFrame;
@@ -212,7 +238,7 @@ namespace Cysharp.Threading.Tasks.Linq
bool completed; bool completed;
bool disposed; bool disposed;
public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken) public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
{ {
if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0; if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0;
if (periodFrameCount != null) if (periodFrameCount != null)
@@ -226,6 +252,15 @@ namespace Cysharp.Threading.Tasks.Linq
this.periodFrameCount = periodFrameCount; this.periodFrameCount = periodFrameCount;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (_TimerFrame)state;
source.completionSource.TrySetCanceled(source.cancellationToken);
}, this);
}
TaskTracker.TrackActiveTask(this, 2); TaskTracker.TrackActiveTask(this, 2);
PlayerLoopHelper.AddAction(updateTiming, this); PlayerLoopHelper.AddAction(updateTiming, this);
} }
@@ -234,13 +269,15 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// return false instead of throw if (disposed || completed) return CompletedTasks.False;
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False;
if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
}
// reset value here. // reset value here.
this.currentFrame = 0; this.currentFrame = 0;
completionSource.Reset(); completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version); return new UniTask<bool>(this, completionSource.Version);
} }
@@ -249,6 +286,7 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
if (!disposed) if (!disposed)
{ {
cancellationTokenRegistration.Dispose();
disposed = true; disposed = true;
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
} }
@@ -257,7 +295,12 @@ namespace Cysharp.Threading.Tasks.Linq
public bool MoveNext() public bool MoveNext()
{ {
if (disposed || cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{
completionSource.TrySetCanceled(cancellationToken);
return false;
}
if (disposed)
{ {
completionSource.TrySetResult(false); completionSource.TrySetResult(false);
return false; return false;

View File

@@ -1,5 +1,11 @@
#pragma warning disable 0649 #pragma warning disable 0649
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
#if SUPPORT_VALUETASK
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Sources; using System.Threading.Tasks.Sources;
@@ -10,7 +16,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static ValueTask AsValueTask(this in UniTask task) public static ValueTask AsValueTask(this in UniTask task)
{ {
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return new ValueTask(new UniTaskValueTaskSource(task), 0); return new ValueTask(new UniTaskValueTaskSource(task), 0);
#else #else
return task; return task;
@@ -19,7 +25,7 @@ namespace Cysharp.Threading.Tasks
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task) public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{ {
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0); return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
#else #else
return task; return task;
@@ -36,7 +42,7 @@ namespace Cysharp.Threading.Tasks
await task; await task;
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
class UniTaskValueTaskSource : IValueTaskSource class UniTaskValueTaskSource : IValueTaskSource
{ {
@@ -95,3 +101,4 @@ namespace Cysharp.Threading.Tasks
#endif #endif
} }
} }
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8760bbbab905a534eb6fb7b61b736926 guid: d38f0478933be42d895c37b862540a1c
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -33,14 +33,14 @@ namespace Cysharp.Threading.Tasks
return new YieldAwaitable(timing); return new YieldAwaitable(timing);
} }
public static UniTask Yield(CancellationToken cancellationToken) public static UniTask Yield(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token); return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken) public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token); return new UniTask(YieldPromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
} }
/// <summary> /// <summary>
@@ -48,7 +48,7 @@ namespace Cysharp.Threading.Tasks
/// </summary> /// </summary>
public static UniTask NextFrame() public static UniTask NextFrame()
{ {
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, out var token), token); return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, false, out var token), token);
} }
/// <summary> /// <summary>
@@ -56,23 +56,23 @@ namespace Cysharp.Threading.Tasks
/// </summary> /// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing) public static UniTask NextFrame(PlayerLoopTiming timing)
{ {
return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, out var token), token); return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, false, out var token), token);
} }
/// <summary> /// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame. /// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary> /// </summary>
public static UniTask NextFrame(CancellationToken cancellationToken) public static UniTask NextFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token); return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
} }
/// <summary> /// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame. /// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary> /// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken) public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token); return new UniTask(NextFramePromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
} }
#if UNITY_2023_1_OR_NEWER #if UNITY_2023_1_OR_NEWER
@@ -88,15 +88,21 @@ namespace Cysharp.Threading.Tasks
} }
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")] [Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken) public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken, cancelImmediately);
} }
#endif #endif
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken = default) public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner)
{ {
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, out var token); var source = WaitForEndOfFramePromise.Create(coroutineRunner, CancellationToken.None, false, out var token);
return new UniTask(source, token);
}
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately = false)
{
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, cancelImmediately, out var token);
return new UniTask(source, token); return new UniTask(source, token);
} }
@@ -113,50 +119,50 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken). /// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
/// </summary> /// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken) public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken, bool cancelImmediately = false)
{ {
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken, cancelImmediately);
} }
public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken); return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken); return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
if (delayFrameCount < 0) if (delayFrameCount < 0)
{ {
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount); throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
} }
return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay); var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken); return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime; var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken); return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay); var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken); return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
} }
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
if (delayTimeSpan < TimeSpan.Zero) if (delayTimeSpan < TimeSpan.Zero)
{ {
@@ -175,16 +181,16 @@ namespace Cysharp.Threading.Tasks
{ {
case DelayType.UnscaledDeltaTime: case DelayType.UnscaledDeltaTime:
{ {
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
case DelayType.Realtime: case DelayType.Realtime:
{ {
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
case DelayType.DeltaTime: case DelayType.DeltaTime:
default: default:
{ {
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token); return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
} }
} }
} }
@@ -201,13 +207,15 @@ namespace Cysharp.Threading.Tasks
} }
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
YieldPromise() YieldPromise()
{ {
} }
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -219,8 +227,17 @@ namespace Cysharp.Threading.Tasks
result = new YieldPromise(); result = new YieldPromise();
} }
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (YieldPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -237,9 +254,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -274,6 +298,8 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -290,14 +316,16 @@ namespace Cysharp.Threading.Tasks
} }
int frameCount; int frameCount;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
NextFramePromise() NextFramePromise()
{ {
} }
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -311,6 +339,16 @@ namespace Cysharp.Threading.Tasks
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (NextFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -327,9 +365,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -369,6 +414,7 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -384,14 +430,16 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
} }
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
WaitForEndOfFramePromise() WaitForEndOfFramePromise()
{ {
} }
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -404,6 +452,16 @@ namespace Cysharp.Threading.Tasks
} }
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitForEndOfFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -420,9 +478,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -446,6 +511,7 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
Reset(); // Reset Enumerator Reset(); // Reset Enumerator
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
return pool.TryPush(this); return pool.TryPush(this);
} }
@@ -494,6 +560,8 @@ namespace Cysharp.Threading.Tasks
int initialFrame; int initialFrame;
int delayFrameCount; int delayFrameCount;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
int currentFrameCount; int currentFrameCount;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
@@ -502,7 +570,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -517,6 +585,16 @@ namespace Cysharp.Threading.Tasks
result.delayFrameCount = delayFrameCount; result.delayFrameCount = delayFrameCount;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayFramePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -533,9 +611,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -604,6 +689,8 @@ namespace Cysharp.Threading.Tasks
currentFrameCount = default; currentFrameCount = default;
delayFrameCount = default; delayFrameCount = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -623,6 +710,8 @@ namespace Cysharp.Threading.Tasks
float delayTimeSpan; float delayTimeSpan;
float elapsed; float elapsed;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@@ -630,7 +719,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -646,6 +735,16 @@ namespace Cysharp.Threading.Tasks
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds; result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -662,9 +761,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -715,6 +821,8 @@ namespace Cysharp.Threading.Tasks
delayTimeSpan = default; delayTimeSpan = default;
elapsed = default; elapsed = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -734,6 +842,8 @@ namespace Cysharp.Threading.Tasks
float elapsed; float elapsed;
int initialFrame; int initialFrame;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@@ -741,7 +851,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -757,6 +867,16 @@ namespace Cysharp.Threading.Tasks
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds; result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1; result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayIgnoreTimeScalePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -773,9 +893,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -826,6 +953,8 @@ namespace Cysharp.Threading.Tasks
delayFrameTimeSpan = default; delayFrameTimeSpan = default;
elapsed = default; elapsed = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -844,6 +973,8 @@ namespace Cysharp.Threading.Tasks
long delayTimeSpanTicks; long delayTimeSpanTicks;
ValueStopwatch stopwatch; ValueStopwatch stopwatch;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
@@ -851,7 +982,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -866,6 +997,16 @@ namespace Cysharp.Threading.Tasks
result.stopwatch = ValueStopwatch.StartNew(); result.stopwatch = ValueStopwatch.StartNew();
result.delayTimeSpanTicks = delayTimeSpan.Ticks; result.delayTimeSpanTicks = delayTimeSpan.Ticks;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (DelayRealtimePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -882,9 +1023,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -931,6 +1079,8 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
stopwatch = default; stopwatch = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@@ -81,6 +81,16 @@ namespace Cysharp.Threading.Tasks
return factory(); return factory();
} }
public static UniTask Create(Func<CancellationToken, UniTask> factory, CancellationToken cancellationToken)
{
return factory(cancellationToken);
}
public static UniTask Create<T>(T state, Func<T, UniTask> factory)
{
return factory(state);
}
public static UniTask<T> Create<T>(Func<UniTask<T>> factory) public static UniTask<T> Create<T>(Func<UniTask<T>> factory)
{ {
return factory(); return factory();
@@ -137,11 +147,19 @@ namespace Cysharp.Threading.Tasks
return () => asyncAction(cancellationToken).Forget(); return () => asyncAction(cancellationToken).Forget();
} }
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// </summary>
public static Action Action<T>(T state, Func<T, UniTaskVoid> asyncAction)
{
return () => asyncAction(state).Forget();
}
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
/// <summary> /// <summary>
/// Create async void(UniTaskVoid) UnityAction. /// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } )) /// For example: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } ))
/// </summary> /// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction) public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction)
{ {
@@ -150,13 +168,94 @@ namespace Cysharp.Threading.Tasks
/// <summary> /// <summary>
/// Create async void(UniTaskVoid) UnityAction. /// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy())) /// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy()))
/// </summary> /// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken) public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{ {
return () => asyncAction(cancellationToken).Forget(); return () => asyncAction(cancellationToken).Forget();
} }
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, Argument))
/// </summary>
public static UnityEngine.Events.UnityAction UnityAction<T>(T state, Func<T, UniTaskVoid> asyncAction)
{
return () => asyncAction(state).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, UniTaskVoid> asyncAction)
{
return (arg) => asyncAction(arg).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, UniTaskVoid> asyncAction)
{
return (arg0, arg1) => asyncAction(arg0, arg1).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, UniTaskVoid> asyncAction)
{
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, UniTaskVoid> asyncAction)
{
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3).Forget();
}
// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg) => asyncAction(arg, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1) => asyncAction(arg0, arg1, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2, cancellationToken).Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3, CancellationToken cancellationToken) => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3, cancellationToken).Forget();
}
#endif #endif
/// <summary> /// <summary>
@@ -175,6 +274,22 @@ namespace Cysharp.Threading.Tasks
return new UniTask<T>(new DeferPromise<T>(factory), 0); return new UniTask<T>(new DeferPromise<T>(factory), 0);
} }
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask Defer<TState>(TState state, Func<TState, UniTask> factory)
{
return new UniTask(new DeferPromiseWithState<TState>(state, factory), 0);
}
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask<TResult> Defer<TState, TResult>(TState state, Func<TState, UniTask<TResult>> factory)
{
return new UniTask<TResult>(new DeferPromiseWithState<TState, TResult>(state, factory), 0);
}
/// <summary> /// <summary>
/// Never complete. /// Never complete.
/// </summary> /// </summary>
@@ -438,6 +553,93 @@ namespace Cysharp.Threading.Tasks
} }
} }
sealed class DeferPromiseWithState<TState> : IUniTaskSource
{
Func<TState, UniTask> factory;
TState argument;
UniTask task;
UniTask.Awaiter awaiter;
public DeferPromiseWithState(TState argument, Func<TState, UniTask> factory)
{
this.argument = argument;
this.factory = factory;
}
public void GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f(argument);
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class DeferPromiseWithState<TState, TResult> : IUniTaskSource<TResult>
{
Func<TState, UniTask<TResult>> factory;
TState argument;
UniTask<TResult> task;
UniTask<TResult>.Awaiter awaiter;
public DeferPromiseWithState(TState argument, Func<TState, UniTask<TResult>> factory)
{
this.argument = argument;
this.factory = factory;
}
public TResult GetResult(short token)
{
return awaiter.GetResult();
}
void IUniTaskSource.GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f(argument);
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class NeverPromise<T> : IUniTaskSource<T> sealed class NeverPromise<T> : IUniTaskSource<T>
{ {
static readonly Action<object> cancellationCallback = CancellationCallback; static readonly Action<object> cancellationCallback = CancellationCallback;

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Threading; using System.Threading;
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
@@ -9,30 +10,40 @@ namespace Cysharp.Threading.Tasks
{ {
public partial struct UniTask public partial struct UniTask
{ {
public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, out var token), token); return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitUntil<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, out var token), token); return new UniTask(WaitUntilPromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update) public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, out var token), token); return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
} }
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WaitWhile<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
return new UniTask(WaitWhilePromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
}
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update, bool completeImmediately = false)
{
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, completeImmediately, out var token), token);
}
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
where T : class where T : class
{ {
var unityObject = target as UnityEngine.Object; var unityObject = target as UnityEngine.Object;
var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null) var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null)
return new UniTask<U>(isUnityObject return new UniTask<U>(isUnityObject
? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out var token) ? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out var token)
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token); : WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out token), token);
} }
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise> sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
@@ -48,6 +59,8 @@ namespace Cysharp.Threading.Tasks
Func<bool> predicate; Func<bool> predicate;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
@@ -55,7 +68,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -69,6 +82,16 @@ namespace Cysharp.Threading.Tasks
result.predicate = predicate; result.predicate = predicate;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilPromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -85,9 +108,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -136,31 +166,36 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
predicate = default; predicate = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise> sealed class WaitUntilPromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise<T>>
{ {
static TaskPool<WaitWhilePromise> pool; static TaskPool<WaitUntilPromise<T>> pool;
WaitWhilePromise nextNode; WaitUntilPromise<T> nextNode;
public ref WaitWhilePromise NextNode => ref nextNode; public ref WaitUntilPromise<T> NextNode => ref nextNode;
static WaitWhilePromise() static WaitUntilPromise()
{ {
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise<T>), () => pool.Size);
} }
Func<bool> predicate; Func<T, bool> predicate;
T argument;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
WaitWhilePromise() WaitUntilPromise()
{ {
} }
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -169,11 +204,22 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result)) if (!pool.TryPop(out var result))
{ {
result = new WaitWhilePromise(); result = new WaitUntilPromise<T>();
} }
result.predicate = predicate; result.predicate = predicate;
result.argument = argument;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilPromise<T>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -190,9 +236,143 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
try
{
if (!predicate(argument))
{
return true;
}
}
catch (Exception ex)
{
core.TrySetException(ex);
return false;
}
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
argument = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
}
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
{
static TaskPool<WaitWhilePromise> pool;
WaitWhilePromise nextNode;
public ref WaitWhilePromise NextNode => ref nextNode;
static WaitWhilePromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
}
Func<bool> predicate;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core;
WaitWhilePromise()
{
}
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitWhilePromise();
}
result.predicate = predicate;
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitWhilePromise)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -241,30 +421,36 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
predicate = default; predicate = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise> sealed class WaitWhilePromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise<T>>
{ {
static TaskPool<WaitUntilCanceledPromise> pool; static TaskPool<WaitWhilePromise<T>> pool;
WaitUntilCanceledPromise nextNode; WaitWhilePromise<T> nextNode;
public ref WaitUntilCanceledPromise NextNode => ref nextNode; public ref WaitWhilePromise<T> NextNode => ref nextNode;
static WaitUntilCanceledPromise() static WaitWhilePromise()
{ {
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise<T>), () => pool.Size);
} }
Func<T, bool> predicate;
T argument;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core; UniTaskCompletionSourceCore<object> core;
WaitUntilCanceledPromise() WaitWhilePromise()
{ {
} }
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, out short token) public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -273,10 +459,22 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result)) if (!pool.TryPop(out var result))
{ {
result = new WaitUntilCanceledPromise(); result = new WaitWhilePromise<T>();
} }
result.predicate = predicate;
result.argument = argument;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitWhilePromise<T>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -293,9 +491,141 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
try
{
if (predicate(argument))
{
return true;
}
}
catch (Exception ex)
{
core.TrySetException(ex);
return false;
}
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
argument = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
}
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
{
static TaskPool<WaitUntilCanceledPromise> pool;
WaitUntilCanceledPromise nextNode;
public ref WaitUntilCanceledPromise NextNode => ref nextNode;
static WaitUntilCanceledPromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
}
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<object> core;
WaitUntilCanceledPromise()
{
}
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitUntilCanceledPromise();
}
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilCanceledPromise)state;
promise.core.TrySetResult(null);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
@@ -329,6 +659,8 @@ namespace Cysharp.Threading.Tasks
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -351,6 +683,8 @@ namespace Cysharp.Threading.Tasks
Func<T, U> monitorFunction; Func<T, U> monitorFunction;
IEqualityComparer<U> equalityComparer; IEqualityComparer<U> equalityComparer;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<U> core; UniTaskCompletionSourceCore<U> core;
@@ -358,7 +692,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -376,6 +710,16 @@ namespace Cysharp.Threading.Tasks
result.currentValue = monitorFunction(target); result.currentValue = monitorFunction(target);
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilValueChangedUnityObjectPromise<T, U>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -392,9 +736,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -453,6 +804,8 @@ namespace Cysharp.Threading.Tasks
monitorFunction = default; monitorFunction = default;
equalityComparer = default; equalityComparer = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }
@@ -474,6 +827,8 @@ namespace Cysharp.Threading.Tasks
Func<T, U> monitorFunction; Func<T, U> monitorFunction;
IEqualityComparer<U> equalityComparer; IEqualityComparer<U> equalityComparer;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<U> core; UniTaskCompletionSourceCore<U> core;
@@ -481,7 +836,7 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -498,6 +853,16 @@ namespace Cysharp.Threading.Tasks
result.currentValue = monitorFunction(target); result.currentValue = monitorFunction(target);
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (WaitUntilValueChangedStandardObjectPromise<T, U>)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -514,9 +879,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -575,6 +947,8 @@ namespace Cysharp.Threading.Tasks
monitorFunction = default; monitorFunction = default;
equalityComparer = default; equalityComparer = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@@ -0,0 +1,183 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public partial struct UniTask
{
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(IEnumerable<UniTask<T>> tasks)
{
return new WhenEachEnumerable<T>(tasks);
}
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(params UniTask<T>[] tasks)
{
return new WhenEachEnumerable<T>(tasks);
}
}
public readonly struct WhenEachResult<T>
{
public T Result { get; }
public Exception Exception { get; }
//[MemberNotNullWhen(false, nameof(Exception))]
public bool IsCompletedSuccessfully => Exception == null;
//[MemberNotNullWhen(true, nameof(Exception))]
public bool IsFaulted => Exception != null;
public WhenEachResult(T result)
{
this.Result = result;
this.Exception = null;
}
public WhenEachResult(Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
this.Result = default;
this.Exception = exception;
}
public void TryThrow()
{
if (IsFaulted)
{
ExceptionDispatchInfo.Capture(Exception).Throw();
}
}
public T GetResult()
{
if (IsFaulted)
{
ExceptionDispatchInfo.Capture(Exception).Throw();
}
return Result;
}
public override string ToString()
{
if (IsCompletedSuccessfully)
{
return Result?.ToString() ?? "";
}
else
{
return $"Exception{{{Exception.Message}}}";
}
}
}
internal enum WhenEachState : byte
{
NotRunning,
Running,
Completed
}
internal sealed class WhenEachEnumerable<T> : IUniTaskAsyncEnumerable<WhenEachResult<T>>
{
IEnumerable<UniTask<T>> source;
public WhenEachEnumerable(IEnumerable<UniTask<T>> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<WhenEachResult<T>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, cancellationToken);
}
sealed class Enumerator : IUniTaskAsyncEnumerator<WhenEachResult<T>>
{
readonly IEnumerable<UniTask<T>> source;
CancellationToken cancellationToken;
Channel<WhenEachResult<T>> channel;
IUniTaskAsyncEnumerator<WhenEachResult<T>> channelEnumerator;
int completeCount;
WhenEachState state;
public Enumerator(IEnumerable<UniTask<T>> source, CancellationToken cancellationToken)
{
this.source = source;
this.cancellationToken = cancellationToken;
}
public WhenEachResult<T> Current => channelEnumerator.Current;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
if (state == WhenEachState.NotRunning)
{
state = WhenEachState.Running;
channel = Channel.CreateSingleConsumerUnbounded<WhenEachResult<T>>();
channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken);
if (source is UniTask<T>[] array)
{
ConsumeAll(this, array, array.Length);
}
else
{
using (var rentArray = ArrayPoolUtil.Materialize(source))
{
ConsumeAll(this, rentArray.Array, rentArray.Length);
}
}
}
return channelEnumerator.MoveNextAsync();
}
static void ConsumeAll(Enumerator self, UniTask<T>[] array, int length)
{
for (int i = 0; i < length; i++)
{
RunWhenEachTask(self, array[i], length).Forget();
}
}
static async UniTaskVoid RunWhenEachTask(Enumerator self, UniTask<T> task, int length)
{
try
{
var result = await task;
self.channel.Writer.TryWrite(new WhenEachResult<T>(result));
}
catch (Exception ex)
{
self.channel.Writer.TryWrite(new WhenEachResult<T>(ex));
}
if (Interlocked.Increment(ref self.completeCount) == length)
{
self.state = WhenEachState.Completed;
self.channel.Writer.TryComplete();
}
}
public async UniTask DisposeAsync()
{
if (channelEnumerator != null)
{
await channelEnumerator.DisposeAsync();
}
if (state != WhenEachState.Completed)
{
state = WhenEachState.Completed;
channel.Writer.TryComplete(new OperationCanceledException());
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 12bdad0556e999f4aa82da29415d361f guid: 7cac24fdda5112047a1cd3dd66b542c4
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -1,6 +1,10 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS0436 #pragma warning disable CS0436
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
#define SUPPORT_VALUETASK
#endif
using Cysharp.Threading.Tasks.CompilerServices; using Cysharp.Threading.Tasks.CompilerServices;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@@ -69,7 +73,7 @@ namespace Cysharp.Threading.Tasks
return new UniTask<bool>(new IsCanceledSource(source), token); return new UniTask<bool>(new IsCanceledSource(source), token);
} }
#if !UNITY_2018_3_OR_NEWER #if SUPPORT_VALUETASK
public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self) public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self)
{ {
@@ -78,7 +82,7 @@ namespace Cysharp.Threading.Tasks
return default; return default;
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return self.AsValueTask(); return self.AsValueTask();
#else #else
return new System.Threading.Tasks.ValueTask(self.source, self.token); return new System.Threading.Tasks.ValueTask(self.source, self.token);
@@ -440,7 +444,7 @@ namespace Cysharp.Threading.Tasks
return self.AsUniTask(); return self.AsUniTask();
} }
#if !UNITY_2018_3_OR_NEWER #if SUPPORT_VALUETASK
public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self) public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self)
{ {
@@ -449,7 +453,7 @@ namespace Cysharp.Threading.Tasks
return new System.Threading.Tasks.ValueTask<T>(self.result); return new System.Threading.Tasks.ValueTask<T>(self.result);
} }
#if NETSTANDARD2_0 #if (UNITASK_NETCORE && NETSTANDARD2_0)
return self.AsValueTask(); return self.AsValueTask();
#else #else
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token); return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);

View File

@@ -201,6 +201,7 @@ namespace Cysharp.Threading.Tasks
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
task.Forget();
return UniTask.FromCanceled(cancellationToken); return UniTask.FromCanceled(cancellationToken);
} }
@@ -224,6 +225,7 @@ namespace Cysharp.Threading.Tasks
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
task.Forget();
return UniTask.FromCanceled<T>(cancellationToken); return UniTask.FromCanceled<T>(cancellationToken);
} }
@@ -454,7 +456,7 @@ namespace Cysharp.Threading.Tasks
} }
/// <summary> /// <summary>
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled). /// Timeout with suppress OperationCanceledException. Returns (bool, IsCanceled).
/// </summary> /// </summary>
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
{ {

View File

@@ -24,12 +24,17 @@ namespace Cysharp.Threading.Tasks
return AwaitForAllAssets(asyncOperation, null, PlayerLoopTiming.Update, cancellationToken: cancellationToken); return AwaitForAllAssets(asyncOperation, null, PlayerLoopTiming.Update, cancellationToken: cancellationToken);
} }
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return AwaitForAllAssets(asyncOperation, progress: null, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct AssetBundleRequestAllAssetsAwaiter : ICriticalNotifyCompletion public struct AssetBundleRequestAllAssetsAwaiter : ICriticalNotifyCompletion
@@ -95,15 +100,20 @@ namespace Cysharp.Threading.Tasks
AssetBundleRequest asyncOperation; AssetBundleRequest asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core; UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
Action<AsyncOperation> continuationAction;
AssetBundleRequestAllAssetsConfiguredSource() AssetBundleRequestAllAssetsConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -118,6 +128,19 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AssetBundleRequestAllAssetsConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -134,9 +157,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -161,6 +191,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
@@ -188,8 +224,28 @@ namespace Cysharp.Threading.Tasks
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.allAssets);
}
}
} }
} }
} }

View File

@@ -20,10 +20,15 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<AsyncGPUReadbackRequest> WithCancellation(this AsyncGPUReadbackRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
if (asyncOperation.done) return UniTask.FromResult(asyncOperation); if (asyncOperation.done) return UniTask.FromResult(asyncOperation);
return new UniTask<AsyncGPUReadbackRequest>(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, out var token), token); return new UniTask<AsyncGPUReadbackRequest>(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, cancelImmediately, out var token), token);
} }
sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource<AsyncGPUReadbackRequest>, IPlayerLoopItem, ITaskPoolNode<AsyncGPUReadbackRequestAwaiterConfiguredSource> sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource<AsyncGPUReadbackRequest>, IPlayerLoopItem, ITaskPoolNode<AsyncGPUReadbackRequestAwaiterConfiguredSource>
@@ -39,15 +44,15 @@ namespace Cysharp.Threading.Tasks
AsyncGPUReadbackRequest asyncOperation; AsyncGPUReadbackRequest asyncOperation;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
UniTaskCompletionSourceCore<AsyncGPUReadbackRequest> core; UniTaskCompletionSourceCore<AsyncGPUReadbackRequest> core;
AsyncGPUReadbackRequestAwaiterConfiguredSource() AsyncGPUReadbackRequestAwaiterConfiguredSource()
{ {
} }
public static IUniTaskSource<AsyncGPUReadbackRequest> Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) public static IUniTaskSource<AsyncGPUReadbackRequest> Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -61,6 +66,16 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var promise = (AsyncGPUReadbackRequestAwaiterConfiguredSource)state;
promise.core.TrySetCanceled(promise.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -77,9 +92,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -131,6 +153,8 @@ namespace Cysharp.Threading.Tasks
core.Reset(); core.Reset();
asyncOperation = default; asyncOperation = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
} }

View File

@@ -0,0 +1,386 @@
// AsyncInstantiateOperation was added since Unity 2022.3.20 / 2023.3.0b7
#if UNITY_2022_3 && !(UNITY_2022_3_0 || UNITY_2022_3_1 || UNITY_2022_3_2 || UNITY_2022_3_3 || UNITY_2022_3_4 || UNITY_2022_3_5 || UNITY_2022_3_6 || UNITY_2022_3_7 || UNITY_2022_3_8 || UNITY_2022_3_9 || UNITY_2022_3_10 || UNITY_2022_3_11 || UNITY_2022_3_12 || UNITY_2022_3_13 || UNITY_2022_3_14 || UNITY_2022_3_15 || UNITY_2022_3_16 || UNITY_2022_3_17 || UNITY_2022_3_18 || UNITY_2022_3_19)
#define UNITY_2022_SUPPORT
#endif
#if UNITY_2022_SUPPORT || UNITY_2023_3_OR_NEWER
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public static class AsyncInstantiateOperationExtensions
{
// AsyncInstantiateOperation<T> has GetAwaiter so no need to impl
// public static UniTask<T[]>.Awaiter GetAwaiter<T>(this AsyncInstantiateOperation<T> operation) where T : Object
public static UniTask<UnityEngine.Object[]> WithCancellation<T>(this AsyncInstantiateOperation asyncOperation, CancellationToken cancellationToken)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<UnityEngine.Object[]> WithCancellation<T>(this AsyncInstantiateOperation asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<UnityEngine.Object[]> ToUniTask(this AsyncInstantiateOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.Result);
return new UniTask<UnityEngine.Object[]>(AsyncInstantiateOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
}
public static UniTask<T[]> WithCancellation<T>(this AsyncInstantiateOperation<T> asyncOperation, CancellationToken cancellationToken)
where T : UnityEngine.Object
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
}
public static UniTask<T[]> WithCancellation<T>(this AsyncInstantiateOperation<T> asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
where T : UnityEngine.Object
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<T[]> ToUniTask<T>(this AsyncInstantiateOperation<T> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
where T : UnityEngine.Object
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.Result);
return new UniTask<T[]>(AsyncInstantiateOperationConfiguredSource<T>.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
}
sealed class AsyncInstantiateOperationConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AsyncInstantiateOperationConfiguredSource>
{
static TaskPool<AsyncInstantiateOperationConfiguredSource> pool;
AsyncInstantiateOperationConfiguredSource nextNode;
public ref AsyncInstantiateOperationConfiguredSource NextNode => ref nextNode;
static AsyncInstantiateOperationConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncInstantiateOperationConfiguredSource), () => pool.Size);
}
AsyncInstantiateOperation asyncOperation;
IProgress<float> progress;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
Action<AsyncOperation> continuationAction;
AsyncInstantiateOperationConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AsyncInstantiateOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncInstantiateOperationConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AsyncInstantiateOperationConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public UnityEngine.Object[] GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
{
progress.Report(asyncOperation.progress);
}
if (asyncOperation.isDone)
{
core.TrySetResult(asyncOperation.Result);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default;
progress = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.Result);
}
}
}
sealed class AsyncInstantiateOperationConfiguredSource<T> : IUniTaskSource<T[]>, IPlayerLoopItem, ITaskPoolNode<AsyncInstantiateOperationConfiguredSource<T>>
where T : UnityEngine.Object
{
static TaskPool<AsyncInstantiateOperationConfiguredSource<T>> pool;
AsyncInstantiateOperationConfiguredSource<T> nextNode;
public ref AsyncInstantiateOperationConfiguredSource<T> NextNode => ref nextNode;
static AsyncInstantiateOperationConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncInstantiateOperationConfiguredSource<T>), () => pool.Size);
}
AsyncInstantiateOperation<T> asyncOperation;
IProgress<float> progress;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<T[]> core;
Action<AsyncOperation> continuationAction;
AsyncInstantiateOperationConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T[]> Create(AsyncInstantiateOperation<T> asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncInstantiateOperationConfiguredSource<T>();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AsyncInstantiateOperationConfiguredSource<T>)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public T[] GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{
TryReturn();
}
else
{
TaskTracker.RemoveTracking(this);
}
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
{
progress.Report(asyncOperation.progress);
}
if (asyncOperation.isDone)
{
core.TrySetResult(asyncOperation.Result);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default;
progress = default;
cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this);
}
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.Result);
}
}
}
}
}
#endif

View File

@@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 3518da33b6245d341a0ef3670ee9268b guid: 8321f4244edfdcd4798b4fcc92a736c9
timeCreated: 1488689723
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@@ -17,7 +17,6 @@ namespace Cysharp.Threading.Tasks
#if !UNITY_2023_1_OR_NEWER #if !UNITY_2023_1_OR_NEWER
// from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine. // from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine.
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation) public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
@@ -30,12 +29,17 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (asyncOperation.isDone) return UniTask.CompletedTask; if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct AsyncOperationAwaiter : ICriticalNotifyCompletion public struct AsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -92,15 +96,20 @@ namespace Cysharp.Threading.Tasks
AsyncOperation asyncOperation; AsyncOperation asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
Action<AsyncOperation> continuationAction;
AsyncOperationConfiguredSource() AsyncOperationConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource Create(AsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource Create(AsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -115,6 +124,19 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AsyncOperationConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -131,9 +153,16 @@ namespace Cysharp.Threading.Tasks
core.GetResult(token); core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
@@ -154,6 +183,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
@@ -178,11 +213,31 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
} }
#endregion #endregion
@@ -200,12 +255,17 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<UnityEngine.Object> WithCancellation(this ResourceRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct ResourceRequestAwaiter : ICriticalNotifyCompletion public struct ResourceRequestAwaiter : ICriticalNotifyCompletion
@@ -266,15 +326,20 @@ namespace Cysharp.Threading.Tasks
ResourceRequest asyncOperation; ResourceRequest asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core; UniTaskCompletionSourceCore<UnityEngine.Object> core;
Action<AsyncOperation> continuationAction;
ResourceRequestConfiguredSource() ResourceRequestConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource<UnityEngine.Object> Create(ResourceRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<UnityEngine.Object> Create(ResourceRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -289,6 +354,19 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (ResourceRequestConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -305,9 +383,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -332,6 +417,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
@@ -356,11 +447,31 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.asset);
}
}
} }
#endregion #endregion
@@ -379,12 +490,17 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<UnityEngine.Object> WithCancellation(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion
@@ -445,15 +561,20 @@ namespace Cysharp.Threading.Tasks
AssetBundleRequest asyncOperation; AssetBundleRequest asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object> core; UniTaskCompletionSourceCore<UnityEngine.Object> core;
Action<AsyncOperation> continuationAction;
AssetBundleRequestConfiguredSource() AssetBundleRequestConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource<UnityEngine.Object> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<UnityEngine.Object> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -468,6 +589,19 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AssetBundleRequestConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -484,9 +618,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -511,6 +652,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
@@ -535,11 +682,31 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.asset);
}
}
} }
#endregion #endregion
@@ -559,12 +726,17 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<AssetBundle> WithCancellation(this AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<AssetBundle>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<AssetBundle>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion
@@ -625,15 +797,20 @@ namespace Cysharp.Threading.Tasks
AssetBundleCreateRequest asyncOperation; AssetBundleCreateRequest asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<AssetBundle> core; UniTaskCompletionSourceCore<AssetBundle> core;
Action<AsyncOperation> continuationAction;
AssetBundleCreateRequestConfiguredSource() AssetBundleCreateRequestConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource<AssetBundle> Create(AssetBundleCreateRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<AssetBundle> Create(AssetBundleCreateRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -648,6 +825,19 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (AssetBundleCreateRequestConfiguredSource)state;
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -664,9 +854,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -691,6 +888,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
@@ -715,11 +918,31 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(asyncOperation.assetBundle);
}
}
} }
#endregion #endregion
@@ -739,7 +962,12 @@ namespace Cysharp.Threading.Tasks
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<UnityWebRequest> WithCancellation(this UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityWebRequest>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityWebRequest>(cancellationToken);
@@ -751,7 +979,7 @@ namespace Cysharp.Threading.Tasks
} }
return UniTask.FromResult(asyncOperation.webRequest); return UniTask.FromResult(asyncOperation.webRequest);
} }
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -820,15 +1048,20 @@ namespace Cysharp.Threading.Tasks
UnityWebRequestAsyncOperation asyncOperation; UnityWebRequestAsyncOperation asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<UnityWebRequest> core; UniTaskCompletionSourceCore<UnityWebRequest> core;
Action<AsyncOperation> continuationAction;
UnityWebRequestAsyncOperationConfiguredSource() UnityWebRequestAsyncOperationConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static IUniTaskSource<UnityWebRequest> Create(UnityWebRequestAsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static IUniTaskSource<UnityWebRequest> Create(UnityWebRequestAsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -843,6 +1076,20 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (UnityWebRequestAsyncOperationConfiguredSource)state;
source.asyncOperation.webRequest.Abort();
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -859,9 +1106,16 @@ namespace Cysharp.Threading.Tasks
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
else
{
TaskTracker.RemoveTracking(this);
}
}
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -886,6 +1140,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
asyncOperation.webRequest.Abort(); asyncOperation.webRequest.Abort();
@@ -900,11 +1160,7 @@ namespace Cysharp.Threading.Tasks
if (asyncOperation.isDone) if (asyncOperation.isDone)
{ {
if (asyncOperation.webRequest == null) if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new ObjectDisposedException("The webRequest has been destroyed."));
}
else if (asyncOperation.webRequest.IsError())
{ {
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest)); core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
} }
@@ -922,11 +1178,35 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
else if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
}
else
{
core.TrySetResult(asyncOperation.webRequest);
}
}
} }
#endregion #endregion

View File

@@ -16,6 +16,7 @@
Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>"; Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>";
Func<string, string> ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>"; Func<string, string> ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>";
Func<(string typeName, string returnType, string returnField), bool> IsAsyncOperationBase = x => x.typeName == "AsyncOperation";
Func<(string typeName, string returnType, string returnField), bool> IsUnityWebRequest = x => x.returnType == "UnityWebRequest"; Func<(string typeName, string returnType, string returnField), bool> IsUnityWebRequest = x => x.returnType == "UnityWebRequest";
Func<(string typeName, string returnType, string returnField), bool> IsAssetBundleModule = x => x.typeName == "AssetBundleRequest" || x.typeName == "AssetBundleCreateRequest"; Func<(string typeName, string returnType, string returnField), bool> IsAssetBundleModule = x => x.typeName == "AssetBundleRequest" || x.typeName == "AssetBundleCreateRequest";
Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void"; Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void";
@@ -43,18 +44,30 @@ namespace Cysharp.Threading.Tasks
<# } #> <# } #>
#region <#= t.typeName #> #region <#= t.typeName #>
<# if (IsAsyncOperationBase(t)) { #>
#if !UNITY_2023_1_OR_NEWER
// from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine.
<# } #>
public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation) public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new <#= t.typeName #>Awaiter(asyncOperation); return new <#= t.typeName #>Awaiter(asyncOperation);
} }
<# if (IsAsyncOperationBase(t)) { #>
#endif
<# } #>
public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken) public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken)
{ {
return ToUniTask(asyncOperation, cancellationToken: cancellationToken); return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
} }
public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
{
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
}
public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<#= IsVoid(t) ? "" : "<" + t.returnType + ">" #>(cancellationToken); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<#= IsVoid(t) ? "" : "<" + t.returnType + ">" #>(cancellationToken);
@@ -70,7 +83,7 @@ namespace Cysharp.Threading.Tasks
<# } else { #> <# } else { #>
if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>; if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
<# } #> <# } #>
return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
} }
public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion
@@ -151,15 +164,20 @@ namespace Cysharp.Threading.Tasks
<#= t.typeName #> asyncOperation; <#= t.typeName #> asyncOperation;
IProgress<float> progress; IProgress<float> progress;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
bool cancelImmediately;
bool completed;
UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core; UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core;
Action<AsyncOperation> continuationAction;
<#= t.typeName #>ConfiguredSource() <#= t.typeName #>ConfiguredSource()
{ {
continuationAction = Continuation;
} }
public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token) public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -174,6 +192,22 @@ namespace Cysharp.Threading.Tasks
result.asyncOperation = asyncOperation; result.asyncOperation = asyncOperation;
result.progress = progress; result.progress = progress;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.cancelImmediately = cancelImmediately;
result.completed = false;
asyncOperation.completed += result.continuationAction;
if (cancelImmediately && cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var source = (<#= t.typeName #>ConfiguredSource)state;
<# if(IsUnityWebRequest(t)) { #>
source.asyncOperation.webRequest.Abort();
<# } #>
source.core.TrySetCanceled(source.cancellationToken);
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
@@ -194,10 +228,13 @@ namespace Cysharp.Threading.Tasks
<# } #> <# } #>
} }
finally finally
{
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
{ {
TryReturn(); TryReturn();
} }
} }
}
<# if (!IsVoid(t)) { #> <# if (!IsVoid(t)) { #>
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
@@ -223,6 +260,12 @@ namespace Cysharp.Threading.Tasks
public bool MoveNext() public bool MoveNext()
{ {
// Already completed
if (completed || asyncOperation == null)
{
return false;
}
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
<# if(IsUnityWebRequest(t)) { #> <# if(IsUnityWebRequest(t)) { #>
@@ -261,11 +304,42 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
asyncOperation.completed -= continuationAction;
asyncOperation = default; asyncOperation = default;
progress = default; progress = default;
cancellationToken = default; cancellationToken = default;
cancellationTokenRegistration.Dispose();
cancelImmediately = default;
return pool.TryPush(this); return pool.TryPush(this);
} }
void Continuation(AsyncOperation _)
{
if (completed)
{
return;
}
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
}
<# if(IsUnityWebRequest(t)) { #>
else if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
}
else
{
core.TrySetResult(asyncOperation.webRequest);
}
<# } else { #>
else
{
core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>);
}
<# } #>
}
} }
#endregion #endregion

View File

@@ -339,7 +339,7 @@ namespace Cysharp.Threading.Tasks
void InvokeCore(string item1, int item2, int item3) void InvokeCore(string item1, int item2, int item3)
{ {
innerEvent.Invoke(item1, item2, item3); Invoke((item1, item2, item3));
} }
public void Dispose() public void Dispose()

View File

@@ -0,0 +1,17 @@
#if UNITY_2023_1_OR_NEWER
namespace Cysharp.Threading.Tasks
{
public static class UnityAwaitableExtensions
{
public static async UniTask AsUniTask(this UnityEngine.Awaitable awaitable)
{
await awaitable;
}
public static async UniTask<T> AsUniTask<T>(this UnityEngine.Awaitable<T> awaitable)
{
return await awaitable;
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c29533c9e4284dee914b71a6579ea274
timeCreated: 1698895807

View File

@@ -2,7 +2,7 @@
"name": "com.cysharp.unitask", "name": "com.cysharp.unitask",
"displayName": "UniTask", "displayName": "UniTask",
"author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" }, "author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" },
"version": "2.4.1", "version": "2.5.10",
"unity": "2018.4", "unity": "2018.4",
"description": "Provides an efficient async/await integration to Unity.", "description": "Provides an efficient async/await integration to Unity.",
"keywords": [ "async/await", "async", "Task", "UniTask" ], "keywords": [ "async/await", "async", "Task", "UniTask" ],

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 959c1472a5d812843bedf9341e87af3b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,133 +0,0 @@
#if UNITY_EDITOR
using System;
using UnityEngine;
namespace RuntimeUnitTestToolkit.Editor
{
// functional declarative construction like flutter.
internal interface IBuilder
{
GameObject GameObject { get; }
T GetComponent<T>();
}
internal class Builder<T> : IBuilder
where T : Component
{
public T Component1 { get; private set; }
public GameObject GameObject { get; private set; }
public Transform Transform { get { return GameObject.transform; } }
public RectTransform RectTransform { get { return GameObject.GetComponent<RectTransform>(); } }
public Action<GameObject> SetTarget
{
set
{
value(this.GameObject);
}
}
public IBuilder Child
{
set
{
value.GameObject.transform.SetParent(GameObject.transform);
}
}
public IBuilder[] Children
{
set
{
foreach (var item in value)
{
item.GameObject.transform.SetParent(GameObject.transform);
}
}
}
public Builder(string name)
{
this.GameObject = new GameObject(name);
this.Component1 = GameObject.AddComponent<T>();
}
public Builder(string name, out T referenceSelf) // out primary reference.
{
this.GameObject = new GameObject(name);
this.Component1 = GameObject.AddComponent<T>();
referenceSelf = this.Component1;
}
public TComponent GetComponent<TComponent>()
{
return this.GameObject.GetComponent<TComponent>();
}
}
internal class Builder<T1, T2> : Builder<T1>
where T1 : Component
where T2 : Component
{
public T2 Component2 { get; private set; }
public Builder(string name)
: base(name)
{
this.Component2 = GameObject.AddComponent<T2>();
}
public Builder(string name, out T1 referenceSelf)
: base(name, out referenceSelf)
{
this.Component2 = GameObject.AddComponent<T2>();
}
}
internal class Builder<T1, T2, T3> : Builder<T1, T2>
where T1 : Component
where T2 : Component
where T3 : Component
{
public T3 Component3 { get; private set; }
public Builder(string name)
: base(name)
{
this.Component3 = GameObject.AddComponent<T3>();
}
public Builder(string name, out T1 referenceSelf)
: base(name, out referenceSelf)
{
this.Component3 = GameObject.AddComponent<T3>();
}
}
internal class Builder<T1, T2, T3, T4> : Builder<T1, T2, T3>
where T1 : Component
where T2 : Component
where T3 : Component
where T4 : Component
{
public T4 Component4 { get; private set; }
public Builder(string name)
: base(name)
{
this.Component4 = GameObject.AddComponent<T4>();
}
public Builder(string name, out T1 referenceSelf)
: base(name, out referenceSelf)
{
this.Component4 = GameObject.AddComponent<T4>();
}
}
}
#endif

View File

@@ -1,345 +0,0 @@
#if UNITY_EDITOR
using UnityEditor;
// Settings MenuItems.
public static partial class UnitTestBuilder
{
[MenuItem("Test/Settings/ScriptBackend/Mono", validate = true, priority = 1)]
static bool ValidateScriptBackendMono()
{
Menu.SetChecked("Test/Settings/ScriptBackend/Mono", LoadOrGetDefaultSettings().ScriptBackend == ScriptingImplementation.Mono2x);
return true;
}
[MenuItem("Test/Settings/ScriptBackend/Mono", validate = false, priority = 1)]
static void ScriptBackendMono()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentScriptBackend = false;
settings.ScriptBackend = ScriptingImplementation.Mono2x;
SaveSettings(settings);
}
[MenuItem("Test/Settings/ScriptBackend/IL2CPP", validate = true, priority = 2)]
static bool ValidateScriptBackendIL2CPP()
{
Menu.SetChecked("Test/Settings/ScriptBackend/IL2CPP", LoadOrGetDefaultSettings().ScriptBackend == ScriptingImplementation.IL2CPP);
return true;
}
[MenuItem("Test/Settings/ScriptBackend/IL2CPP", validate = false, priority = 2)]
static void ScriptBackendIL2CPP()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentScriptBackend = false;
settings.ScriptBackend = ScriptingImplementation.IL2CPP;
SaveSettings(settings);
}
[MenuItem("Test/Settings/AutoRunPlayer", validate = true, priority = 3)]
static bool ValidateAutoRun()
{
Menu.SetChecked("Test/Settings/AutoRunPlayer", LoadOrGetDefaultSettings().AutoRunPlayer);
return true;
}
[MenuItem("Test/Settings/AutoRunPlayer", validate = false, priority = 3)]
static void AutoRun()
{
var settings = LoadOrGetDefaultSettings();
settings.AutoRunPlayer = !settings.AutoRunPlayer;
SaveSettings(settings);
}
[MenuItem("Test/Settings/Headless", validate = true, priority = 4)]
static bool ValidateHeadless()
{
Menu.SetChecked("Test/Settings/Headless", LoadOrGetDefaultSettings().Headless);
return true;
}
[MenuItem("Test/Settings/Headless", validate = false, priority = 4)]
static void Headless()
{
var settings = LoadOrGetDefaultSettings();
settings.Headless = !settings.Headless;
SaveSettings(settings);
}
[MenuItem("Test/Settings/DisableAutoClose", validate = true, priority = 5)]
static bool ValidateDisableAutoClose()
{
Menu.SetChecked("Test/Settings/DisableAutoClose", LoadOrGetDefaultSettings().DisableAutoClose);
return true;
}
[MenuItem("Test/Settings/DisableAutoClose", validate = false, priority = 5)]
static void DisableAutoClose()
{
var settings = LoadOrGetDefaultSettings();
settings.DisableAutoClose = !settings.DisableAutoClose;
SaveSettings(settings);
}
// generated
/*
*
void Main()
{
var sb = new StringBuilder();
var p = 1;
foreach (var target in Enum.GetNames(typeof(BuildTarget)))
{
var path = $"Test/Settings/BuildTarget/{target}";
var priority = p++;
var template = $@"
[MenuItem(""{path}"", validate = true, priority = {priority})]
static bool ValidateBuildTarget{target}()
{{
Menu.SetChecked(""{path}"", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.{target});
return true;
}}
[MenuItem(""{path}"", validate = false, priority = {priority})]
static void BuildTarget{target}()
{{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.{target};
SaveSettings(settings);
}}";
sb.AppendLine(template);
}
sb.ToString().Dump();
}
public enum BuildTarget
{
StandaloneWindows,
StandaloneWindows64,
StandaloneLinux,
StandaloneLinux64,
StandaloneOSX,
WebGL,
iOS,
Android,
WSAPlayer,
PS4,
XboxOne,
Switch,
}
*/
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows", validate = true, priority = 1)]
static bool ValidateBuildTargetStandaloneWindows()
{
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneWindows", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneWindows);
return true;
}
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows", validate = false, priority = 1)]
static void BuildTargetStandaloneWindows()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.StandaloneWindows;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows64", validate = true, priority = 2)]
static bool ValidateBuildTargetStandaloneWindows64()
{
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneWindows64", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneWindows64);
return true;
}
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows64", validate = false, priority = 2)]
static void BuildTargetStandaloneWindows64()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.StandaloneWindows64;
SaveSettings(settings);
}
#if !UNITY_2019_2_OR_NEWER
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)]
static bool ValidateBuildTargetStandaloneLinux()
{
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux);
return true;
}
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = false, priority = 3)]
static void BuildTargetStandaloneLinux()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.StandaloneLinux;
SaveSettings(settings);
}
#endif
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)]
static bool ValidateBuildTargetStandaloneLinux64()
{
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux64", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux64);
return true;
}
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = false, priority = 4)]
static void BuildTargetStandaloneLinux64()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.StandaloneLinux64;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/StandaloneOSX", validate = true, priority = 5)]
static bool ValidateBuildTargetStandaloneOSX()
{
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneOSX", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneOSX);
return true;
}
[MenuItem("Test/Settings/BuildTarget/StandaloneOSX", validate = false, priority = 5)]
static void BuildTargetStandaloneOSX()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.StandaloneOSX;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/WebGL", validate = true, priority = 6)]
static bool ValidateBuildTargetWebGL()
{
Menu.SetChecked("Test/Settings/BuildTarget/WebGL", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.WebGL);
return true;
}
[MenuItem("Test/Settings/BuildTarget/WebGL", validate = false, priority = 6)]
static void BuildTargetWebGL()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.WebGL;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/iOS", validate = true, priority = 7)]
static bool ValidateBuildTargetiOS()
{
Menu.SetChecked("Test/Settings/BuildTarget/iOS", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.iOS);
return true;
}
[MenuItem("Test/Settings/BuildTarget/iOS", validate = false, priority = 7)]
static void BuildTargetiOS()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.iOS;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/Android", validate = true, priority = 8)]
static bool ValidateBuildTargetAndroid()
{
Menu.SetChecked("Test/Settings/BuildTarget/Android", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.Android);
return true;
}
[MenuItem("Test/Settings/BuildTarget/Android", validate = false, priority = 8)]
static void BuildTargetAndroid()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.Android;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/WSAPlayer", validate = true, priority = 9)]
static bool ValidateBuildTargetWSAPlayer()
{
Menu.SetChecked("Test/Settings/BuildTarget/WSAPlayer", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.WSAPlayer);
return true;
}
[MenuItem("Test/Settings/BuildTarget/WSAPlayer", validate = false, priority = 9)]
static void BuildTargetWSAPlayer()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.WSAPlayer;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/PS4", validate = true, priority = 10)]
static bool ValidateBuildTargetPS4()
{
Menu.SetChecked("Test/Settings/BuildTarget/PS4", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.PS4);
return true;
}
[MenuItem("Test/Settings/BuildTarget/PS4", validate = false, priority = 10)]
static void BuildTargetPS4()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.PS4;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/XboxOne", validate = true, priority = 11)]
static bool ValidateBuildTargetXboxOne()
{
Menu.SetChecked("Test/Settings/BuildTarget/XboxOne", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.XboxOne);
return true;
}
[MenuItem("Test/Settings/BuildTarget/XboxOne", validate = false, priority = 11)]
static void BuildTargetXboxOne()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.XboxOne;
SaveSettings(settings);
}
[MenuItem("Test/Settings/BuildTarget/Switch", validate = true, priority = 12)]
static bool ValidateBuildTargetSwitch()
{
Menu.SetChecked("Test/Settings/BuildTarget/Switch", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.Switch);
return true;
}
[MenuItem("Test/Settings/BuildTarget/Switch", validate = false, priority = 12)]
static void BuildTargetSwitch()
{
var settings = LoadOrGetDefaultSettings();
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = BuildTarget.Switch;
SaveSettings(settings);
}
}
#endif

View File

@@ -1,546 +0,0 @@
#if UNITY_EDITOR
using RuntimeUnitTestToolkit;
using RuntimeUnitTestToolkit.Editor;
using System;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
internal class RuntimeUnitTestSettings
{
public ScriptingImplementation ScriptBackend;
public bool UseCurrentScriptBackend;
public BuildTarget BuildTarget;
public bool UseCurrentBuildTarget;
public bool Headless;
public bool AutoRunPlayer;
public bool DisableAutoClose;
public RuntimeUnitTestSettings()
{
UseCurrentBuildTarget = true;
UseCurrentScriptBackend = true;
Headless = false;
AutoRunPlayer = true;
DisableAutoClose = false;
}
public override string ToString()
{
return $"{ScriptBackend} {BuildTarget} Headless:{Headless} AutoRunPlayer:{AutoRunPlayer} DisableAutoClose:{DisableAutoClose}";
}
}
// no namespace(because invoke from commandline)
public static partial class UnitTestBuilder
{
const string SettingsKeyBase = "RuntimeUnitTest.Settings.";
[MenuItem("Test/BuildUnitTest")]
public static void BuildUnitTest()
{
var settings = new RuntimeUnitTestSettings(); // default
string buildPath = null;
if (Application.isBatchMode) // from commandline
{
settings.AutoRunPlayer = false;
settings.DisableAutoClose = false;
var cmdArgs = Environment.GetCommandLineArgs();
for (int i = 0; i < cmdArgs.Length; i++)
{
if (string.Equals(cmdArgs[i].Trim('-', '/'), "ScriptBackend", StringComparison.OrdinalIgnoreCase))
{
settings.UseCurrentScriptBackend = false;
var str = cmdArgs[++i];
if (str.StartsWith("mono", StringComparison.OrdinalIgnoreCase))
{
settings.ScriptBackend = ScriptingImplementation.Mono2x;
}
else if (str.StartsWith("IL2CPP", StringComparison.OrdinalIgnoreCase))
{
settings.ScriptBackend = ScriptingImplementation.IL2CPP;
}
else
{
settings.ScriptBackend = (ScriptingImplementation)Enum.Parse(typeof(ScriptingImplementation), str, true);
}
}
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "BuildTarget", StringComparison.OrdinalIgnoreCase))
{
settings.UseCurrentBuildTarget = false;
settings.BuildTarget = (BuildTarget)Enum.Parse(typeof(BuildTarget), cmdArgs[++i], true);
}
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "Headless", StringComparison.OrdinalIgnoreCase))
{
settings.Headless = true;
}
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "buildPath", StringComparison.OrdinalIgnoreCase))
{
buildPath = cmdArgs[++i];
}
}
}
else
{
var key = SettingsKeyBase + Application.productName;
var settingsValue = EditorPrefs.GetString(key, null);
try
{
if (!string.IsNullOrWhiteSpace(settingsValue))
{
settings = JsonUtility.FromJson<RuntimeUnitTestSettings>(settingsValue);
}
}
catch
{
UnityEngine.Debug.LogError("Fail to load RuntimeUnitTest settings");
EditorPrefs.SetString(key, null);
}
}
if (settings.UseCurrentBuildTarget)
{
settings.BuildTarget = EditorUserBuildSettings.activeBuildTarget;
}
if (settings.UseCurrentScriptBackend)
{
settings.ScriptBackend = PlayerSettings.GetScriptingBackend(ToBuildTargetGroup(settings.BuildTarget));
}
if (buildPath == null)
{
buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + GetExtensionForBuildTarget(settings.BuildTarget);
}
var originalScene = SceneManager.GetActiveScene().path;
BuildUnitTest(buildPath, settings.ScriptBackend, settings.BuildTarget, settings.Headless, settings.AutoRunPlayer, settings.DisableAutoClose);
// reopen original scene
if (!string.IsNullOrWhiteSpace(originalScene))
{
EditorSceneManager.OpenScene(originalScene, OpenSceneMode.Single);
}
else
{
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
}
}
[MenuItem("Test/LoadUnitTestScene")]
public static void LoadUnitTestScene()
{
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
BuildUnitTestRunnerScene();
EditorSceneManager.MarkSceneDirty(scene);
}
static RuntimeUnitTestSettings LoadOrGetDefaultSettings()
{
var key = SettingsKeyBase + Application.productName;
var settingsValue = EditorPrefs.GetString(key, null);
RuntimeUnitTestSettings settings = null;
try
{
if (!string.IsNullOrWhiteSpace(settingsValue))
{
settings = JsonUtility.FromJson<RuntimeUnitTestSettings>(settingsValue);
}
}
catch
{
UnityEngine.Debug.LogError("Fail to load RuntimeUnitTest settings");
EditorPrefs.SetString(key, null);
settings = null;
}
if (settings == null)
{
// default
settings = new RuntimeUnitTestSettings
{
UseCurrentBuildTarget = true,
UseCurrentScriptBackend = true,
Headless = false,
AutoRunPlayer = true,
};
}
return settings;
}
static void SaveSettings(RuntimeUnitTestSettings settings)
{
var key = SettingsKeyBase + Application.productName;
EditorPrefs.SetString(key, JsonUtility.ToJson(settings));
}
public static void BuildUnitTest(string buildPath, ScriptingImplementation scriptBackend, BuildTarget buildTarget, bool headless, bool autoRunPlayer, bool disableAutoClose)
{
var sceneName = "Assets/TempRuntimeUnitTestScene_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (disableAutoClose)
{
sceneName += "_DisableAutoClose";
}
sceneName += ".unity";
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
BuildUnitTestRunnerScene();
EditorSceneManager.MarkSceneDirty(scene);
AssetDatabase.SaveAssets();
EditorSceneManager.SaveScene(scene, sceneName, false);
try
{
Build(sceneName, buildPath, new RuntimeUnitTestSettings { ScriptBackend = scriptBackend, BuildTarget = buildTarget, Headless = headless, AutoRunPlayer = autoRunPlayer, DisableAutoClose = disableAutoClose });
}
finally
{
AssetDatabase.DeleteAsset(sceneName);
}
}
public static UnitTestRunner BuildUnitTestRunnerScene()
{
const string kStandardSpritePath = "UI/Skin/UISprite.psd";
const string kBackgroundSpritePath = "UI/Skin/Background.psd";
var uisprite = AssetDatabase.GetBuiltinExtraResource<Sprite>(kStandardSpritePath);
var background = AssetDatabase.GetBuiltinExtraResource<Sprite>(kBackgroundSpritePath);
ScrollRect buttonList;
VerticalLayoutGroup listLayout;
Scrollbar refListScrollbar;
ScrollRect logList;
Scrollbar refLogScrollbar;
Button clearButton;
Text logText;
// Flutter like coded build utility
var rootObject = new Builder<Camera>("SceneRoot")
{
Children = new IBuilder[] {
new Builder<EventSystem, StandaloneInputModule>("EventSystem"),
new Builder<Canvas, CanvasScaler, GraphicRaycaster>("Canvas") {
Component1 = { renderMode = RenderMode.ScreenSpaceOverlay },
Children = new IBuilder[] {
new Builder<HorizontalLayoutGroup, CanvasRenderer>("HorizontalSplitter") {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
Component1 = { childControlWidth = true, childControlHeight = true, spacing = 10 },
Children = new IBuilder[] {
new Builder<ScrollRect, CanvasRenderer>("ButtonList", out buttonList) {
RectTransform = { pivot = new Vector2(0.5f, 0.5f) },
Component1 = { horizontal =false, vertical = true, movementType = ScrollRect.MovementType.Clamped },
Children = new IBuilder[] {
new Builder<VerticalLayoutGroup, ContentSizeFitter>("ListLayoutToAttach", out listLayout) {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0, 1) },
Component1 = { childControlWidth = true, childControlHeight = true, childForceExpandWidth = true, childForceExpandHeight = false, spacing = 10, padding = new RectOffset(10,20,10,10) },
Component2 = { horizontalFit = ContentSizeFitter.FitMode.Unconstrained, verticalFit = ContentSizeFitter.FitMode.PreferredSize },
SetTarget = self => { buttonList.content = self.GetComponent<RectTransform>(); },
Child = new Builder<Button, Image, LayoutElement>("ClearButton", out clearButton) {
Component2 = { sprite = uisprite, type = Image.Type.Sliced },
Component3 = { minHeight = 50 },
SetTarget = self => { self.GetComponent<Button>().targetGraphic = self.GetComponent<Graphic>(); },
Child = new Builder<Text>("ButtonText") {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0.5f, 0.5f) },
Component1 = { text = "Clear", color = FromRGB(50, 50, 50), alignment = TextAnchor.MiddleCenter, fontSize = 24, lineSpacing = 1 }
}
}
},
new Builder<Scrollbar,Image>("ListScrollbar", out refListScrollbar) {
RectTransform = { anchorMin = new Vector2(1, 0), anchorMax = new Vector2(1, 1) },
Component1 = { navigation = new Navigation{ mode = Navigation.Mode.None }, direction = Scrollbar.Direction.BottomToTop, size = 1.0f },
Component2 = { sprite = background, type = Image.Type.Sliced },
SetTarget = self => { buttonList.verticalScrollbar = self.GetComponent<Scrollbar>(); },
Child = new Builder<RectTransform>("Sliding Area") {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
Child = new Builder<Image>("Handle") {
Component1 = { sprite = uisprite, type = Image.Type.Sliced },
SetTarget = self =>
{
refListScrollbar.targetGraphic = self.GetComponent<Graphic>();
refListScrollbar.handleRect = self.GetComponent<RectTransform>();
}
}
}
}
}
},
new Builder<ScrollRect, CanvasRenderer>("ScrollableText", out logList) {
RectTransform = { pivot = new Vector2(0.5f, 0.5f) },
Component1 = { horizontal =false, vertical = true, movementType = ScrollRect.MovementType.Elastic, elasticity = 0.1f },
Children = new IBuilder[] {
new Builder<Text, ContentSizeFitter>("Log", out logText) {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0, 1) },
Component1 = { fontSize = 24, lineSpacing = 1, supportRichText = true, alignment = TextAnchor.UpperLeft, horizontalOverflow = HorizontalWrapMode.Wrap, verticalOverflow = VerticalWrapMode.Truncate },
Component2 = { horizontalFit = ContentSizeFitter.FitMode.Unconstrained, verticalFit = ContentSizeFitter.FitMode.PreferredSize },
SetTarget = self => { logList.content = self.GetComponent<RectTransform>(); }
},
new Builder<Scrollbar,Image>("LogScrollbar", out refLogScrollbar) {
RectTransform = { anchorMin = new Vector2(1, 0), anchorMax = new Vector2(1, 1) },
Component1 = { navigation = new Navigation{ mode = Navigation.Mode.None }, direction = Scrollbar.Direction.BottomToTop, size = 1.0f },
Component2 = { sprite = background, type = Image.Type.Sliced },
SetTarget = self => { logList.verticalScrollbar = self.GetComponent<Scrollbar>(); },
Child = new Builder<RectTransform>("Sliding Area2") {
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
Child = new Builder<Image>("Handle2") {
Component1 = { sprite = uisprite, type = Image.Type.Sliced },
SetTarget = self =>
{
refLogScrollbar.targetGraphic = self.GetComponent<Graphic>();
refLogScrollbar.handleRect = self.GetComponent<RectTransform>();
}
}
}
}
}
},
}
}
}
}
}
};
// size modify after build complete:)
{
var rect = GameObject.Find("HorizontalSplitter").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
}
{
var rect = GameObject.Find("ListLayoutToAttach").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
}
{
var rect = GameObject.Find("ListScrollbar").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(30, 0);
}
{
var rect = GameObject.Find("ClearButton").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
}
{
var rect = GameObject.Find("Sliding Area").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(-20, -20);
}
{
var rect = GameObject.Find("Handle").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(20, 20);
}
{
var rect = GameObject.Find("ButtonText").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
}
{
var rect = GameObject.Find("Log").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(15, 0);
rect.offsetMax = new Vector2(-20, 0);
}
{
var rect = GameObject.Find("LogScrollbar").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(-30, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(30, 0);
}
{
var rect = GameObject.Find("Sliding Area2").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(-20, -20);
}
{
var rect = GameObject.Find("Handle2").GetComponent<RectTransform>();
rect.offsetMin = new Vector2(0, 0);
rect.offsetMax = new Vector2(0, 0);
rect.sizeDelta = new Vector2(20, 20);
}
// add test script
var runner = rootObject.GameObject.AddComponent<UnitTestRunner>();
runner.clearButton = clearButton;
runner.list = listLayout.gameObject.GetComponent<RectTransform>();
runner.listScrollBar = refListScrollbar;
runner.logText = logText;
runner.logScrollBar = refLogScrollbar;
return runner;
}
static void Build(string sceneName, string buildPath, RuntimeUnitTestSettings settings)
{
var options = BuildOptions.BuildScriptsOnly | BuildOptions.IncludeTestAssemblies;
if (settings.AutoRunPlayer)
{
options |= BuildOptions.AutoRunPlayer;
}
if (settings.Headless)
{
options |= BuildOptions.EnableHeadlessMode;
}
var targetGroup = ToBuildTargetGroup(settings.BuildTarget);
var currentBackend = PlayerSettings.GetScriptingBackend(targetGroup);
if (currentBackend != settings.ScriptBackend)
{
UnityEngine.Debug.Log("Modify ScriptBackend to " + settings.ScriptBackend);
PlayerSettings.SetScriptingBackend(targetGroup, settings.ScriptBackend);
}
var buildOptions = new BuildPlayerOptions
{
target = settings.BuildTarget,
targetGroup = targetGroup,
options = options,
scenes = new[] { sceneName },
locationPathName = buildPath
};
UnityEngine.Debug.Log("UnitTest Build Start, " + settings.ToString());
var buildReport = BuildPipeline.BuildPlayer(buildOptions);
if (currentBackend != settings.ScriptBackend)
{
UnityEngine.Debug.Log("Restore ScriptBackend to " + currentBackend);
PlayerSettings.SetScriptingBackend(targetGroup, currentBackend);
}
if (buildReport.summary.result != BuildResult.Succeeded)
{
// Note: show error summary?
// Debug.LogError(buildReport.SummarizeErrors());
UnityEngine.Debug.LogError("UnitTest Build Failed.");
}
else
{
UnityEngine.Debug.Log("UnitTest Build Completed, binary located: " + buildOptions.locationPathName);
}
}
static Color FromRGB(int r, int g, int b)
{
return new Color(r / 255f, g / 255f, b / 255f);
}
static bool IsWindows(BuildTarget buildTarget)
{
switch (buildTarget)
{
case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64:
case BuildTarget.WSAPlayer:
return true;
default:
return false;
}
}
static string GetExtensionForBuildTarget(BuildTarget buildTarget)
{
switch (buildTarget)
{
case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64:
case BuildTarget.WSAPlayer:
return ".exe";
case BuildTarget.StandaloneOSX:
return ".app";
case BuildTarget.Android:
return ".apk";
default:
return "";
}
}
static BuildTargetGroup ToBuildTargetGroup(BuildTarget buildTarget)
{
#pragma warning disable CS0618
switch (buildTarget)
{
#if UNITY_2017_3_OR_NEWER
case BuildTarget.StandaloneOSX:
#else
case BuildTarget.StandaloneOSXIntel:
case BuildTarget.StandaloneOSXIntel64:
case BuildTarget.StandaloneOSXUniversal:
#endif // UNITY_2017_3_OR_NEWER
case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64:
case BuildTarget.StandaloneLinux64:
#if !UNITY_2019_2_OR_NEWER
case BuildTarget.StandaloneLinux:
case BuildTarget.StandaloneLinuxUniversal:
#endif // !UNITY_2019_2_OR_NEWER
return BuildTargetGroup.Standalone;
case (BuildTarget)6:
case (BuildTarget)7:
case BuildTarget.WebGL:
return BuildTargetGroup.WebGL;
case BuildTarget.iOS:
return BuildTargetGroup.iOS;
case BuildTarget.PS3:
return BuildTargetGroup.PS3;
case BuildTarget.PS4:
return BuildTargetGroup.PS4;
case BuildTarget.XBOX360:
return BuildTargetGroup.XBOX360;
case BuildTarget.Android:
return BuildTargetGroup.Android;
case BuildTarget.WSAPlayer:
return BuildTargetGroup.WSA;
case BuildTarget.WP8Player:
return BuildTargetGroup.WP8;
case BuildTarget.Tizen:
return BuildTargetGroup.Tizen;
case BuildTarget.PSP2:
return BuildTargetGroup.PSP2;
case BuildTarget.PSM:
return BuildTargetGroup.PSM;
case BuildTarget.XboxOne:
return BuildTargetGroup.XboxOne;
case BuildTarget.SamsungTV:
return BuildTargetGroup.SamsungTV;
case BuildTarget.N3DS:
return BuildTargetGroup.N3DS;
case BuildTarget.WiiU:
return BuildTargetGroup.WiiU;
case BuildTarget.tvOS:
return BuildTargetGroup.tvOS;
case BuildTarget.Switch:
return BuildTargetGroup.Switch;
case BuildTarget.Lumin:
return BuildTargetGroup.Lumin;
case BuildTarget.BlackBerry:
return BuildTargetGroup.BlackBerry;
case BuildTarget.NoTarget:
default:
return BuildTargetGroup.Unknown;
}
#pragma warning restore CS0618
}
}
#endif

View File

@@ -1,16 +0,0 @@
{
"name": "RuntimeUnitTestToolkit",
"references": [
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 14c4fea4b238088479114ba2ffe195f9
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,684 +0,0 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.TestTools;
using UnityEngine.UI;
namespace RuntimeUnitTestToolkit
{
public class UnitTestRunner : MonoBehaviour
{
// object is IEnumerator or Func<IEnumerator>
Dictionary<string, List<TestKeyValuePair>> tests = new Dictionary<string, List<TestKeyValuePair>>();
List<Pair> additionalActionsOnFirst = new List<Pair>();
public Button clearButton;
public RectTransform list;
public Scrollbar listScrollBar;
public Text logText;
public Scrollbar logScrollBar;
readonly Color passColor = new Color(0f, 1f, 0f, 1f); // green
readonly Color failColor = new Color(1f, 0f, 0f, 1f); // red
readonly Color normalColor = new Color(1f, 1f, 1f, 1f); // white
bool allTestGreen = true;
bool logClear = false;
void Start()
{
try
{
UnityEngine.Application.logMessageReceived += (a, b, c) =>
{
if (a.Contains("Mesh can not have more than 65000 vertices"))
{
logClear = true;
}
else
{
AppendToGraphicText("[" + c + "]" + a + "\n");
WriteToConsole("[" + c + "]" + a);
}
};
// register all test types
foreach (var item in GetTestTargetTypes())
{
RegisterAllMethods(item);
}
var executeAll = new List<Func<Coroutine>>();
foreach (var ___item in tests)
{
var actionList = ___item; // be careful, capture in lambda
executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList)));
Add(actionList.Key, () => StartCoroutine(RunTestInCoroutine(actionList)));
}
var executeAllButton = Add("Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll)));
clearButton.gameObject.GetComponent<Image>().color = new Color(170 / 255f, 170 / 255f, 170 / 255f, 1);
executeAllButton.gameObject.GetComponent<Image>().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1);
executeAllButton.transform.SetSiblingIndex(1);
additionalActionsOnFirst.Reverse();
foreach (var item in additionalActionsOnFirst)
{
var newButton = GameObject.Instantiate(clearButton);
newButton.name = item.Name;
newButton.onClick.RemoveAllListeners();
newButton.GetComponentInChildren<Text>().text = item.Name;
newButton.onClick.AddListener(item.Action);
newButton.transform.SetParent(list);
newButton.transform.SetSiblingIndex(1);
}
clearButton.onClick.AddListener(() =>
{
logText.text = "";
foreach (var btn in list.GetComponentsInChildren<Button>())
{
btn.interactable = true;
btn.GetComponent<Image>().color = normalColor;
}
executeAllButton.gameObject.GetComponent<Image>().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1);
});
listScrollBar.value = 1;
logScrollBar.value = 1;
if (Application.isBatchMode)
{
// run immediately in player
StartCoroutine(ExecuteAllInCoroutine(executeAll));
}
}
catch (Exception ex)
{
if (Application.isBatchMode)
{
// when failed(can not start runner), quit immediately.
WriteToConsole(ex.ToString());
Application.Quit(1);
}
else
{
throw;
}
}
}
Button Add(string title, UnityAction test)
{
var newButton = GameObject.Instantiate(clearButton);
newButton.name = title;
newButton.onClick.RemoveAllListeners();
newButton.GetComponentInChildren<Text>().text = title;
newButton.onClick.AddListener(test);
newButton.transform.SetParent(list);
return newButton;
}
static IEnumerable<Type> GetTestTargetTypes()
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var n = assembly.FullName;
if (n.StartsWith("UnityEngine")) continue;
if (n.StartsWith("mscorlib")) continue;
if (n.StartsWith("System")) continue;
foreach (var item in assembly.GetTypes())
{
foreach (var method in item.GetMethods())
{
TestAttribute t1 = null;
try
{
t1 = method.GetCustomAttribute<TestAttribute>(true);
}
catch (Exception ex)
{
Debug.Log("TestAttribute Load Fail, Assembly:" + assembly.FullName);
Debug.LogException(ex);
goto NEXT_ASSEMBLY;
}
if (t1 != null)
{
yield return item;
break;
}
UnityTestAttribute t2 = null;
try
{
t2 = method.GetCustomAttribute<UnityTestAttribute>(true);
}
catch (Exception ex)
{
Debug.Log("UnityTestAttribute Load Fail, Assembly:" + assembly.FullName);
Debug.LogException(ex);
goto NEXT_ASSEMBLY;
}
if (t2 != null)
{
yield return item;
break;
}
}
}
NEXT_ASSEMBLY:
continue;
}
}
public void AddTest(string group, string title, Action test, List<Action> setups, List<Action> teardowns)
{
List<TestKeyValuePair> list;
if (!tests.TryGetValue(group, out list))
{
list = new List<TestKeyValuePair>();
tests[group] = list;
}
list.Add(new TestKeyValuePair(title, test, setups, teardowns));
}
public void AddAsyncTest(string group, string title, Func<IEnumerator> asyncTestCoroutine, List<Action> setups, List<Action> teardowns)
{
List<TestKeyValuePair> list;
if (!tests.TryGetValue(group, out list))
{
list = new List<TestKeyValuePair>();
tests[group] = list;
}
list.Add(new TestKeyValuePair(title, asyncTestCoroutine, setups, teardowns));
}
public void AddCutomAction(string name, UnityAction action)
{
additionalActionsOnFirst.Add(new Pair { Name = name, Action = action });
}
public void RegisterAllMethods<T>()
where T : new()
{
RegisterAllMethods(typeof(T));
}
public void RegisterAllMethods(Type testType)
{
try
{
var test = Activator.CreateInstance(testType);
var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
List<Action> setups = new List<Action>();
List<Action> teardowns = new List<Action>();
foreach (var item in methods)
{
try
{
var setup = item.GetCustomAttribute<NUnit.Framework.SetUpAttribute>(true);
if (setup != null)
{
setups.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item));
}
var teardown = item.GetCustomAttribute<NUnit.Framework.TearDownAttribute>(true);
if (teardown != null)
{
teardowns.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item));
}
}
catch (Exception e)
{
UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register setup/teardown method, exception: " + e.ToString());
}
}
foreach (var item in methods)
{
try
{
var iteratorTest = item.GetCustomAttribute<UnityEngine.TestTools.UnityTestAttribute>(true);
if (iteratorTest != null)
{
if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator))
{
var factory = (Func<IEnumerator>)Delegate.CreateDelegate(typeof(Func<IEnumerator>), test, item);
AddAsyncTest(factory.Target.GetType().Name, factory.Method.Name, factory, setups, teardowns);
}
else
{
var testData = GetTestData(item);
if (testData.Count != 0)
{
foreach (var item2 in testData)
{
Func<IEnumerator> factory;
if (item.IsGenericMethod)
{
var method2 = InferGenericType(item, item2);
factory = () => (IEnumerator)method2.Invoke(test, item2);
}
else
{
factory = () => (IEnumerator)item.Invoke(test, item2);
}
var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")";
name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]");
AddAsyncTest(test.GetType().Name, name, factory, setups, teardowns);
}
}
else
{
UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid).");
}
}
}
var standardTest = item.GetCustomAttribute<NUnit.Framework.TestAttribute>(true);
if (standardTest != null)
{
if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void))
{
var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item);
AddTest(invoke.Target.GetType().Name, invoke.Method.Name, invoke, setups, teardowns);
}
else
{
var testData = GetTestData(item);
if (testData.Count != 0)
{
foreach (var item2 in testData)
{
Action invoke = null;
if (item.IsGenericMethod)
{
var method2 = InferGenericType(item, item2);
invoke = () => method2.Invoke(test, item2);
}
else
{
invoke = () => item.Invoke(test, item2);
}
var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")";
name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]");
AddTest(test.GetType().Name, name, invoke, setups, teardowns);
}
}
else
{
UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid).");
}
}
}
}
catch (Exception e)
{
UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString());
}
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
List<object[]> GetTestData(MethodInfo methodInfo)
{
List<object[]> testCases = new List<object[]>();
var inlineData = methodInfo.GetCustomAttributes<NUnit.Framework.TestCaseAttribute>(true);
foreach (var item in inlineData)
{
testCases.Add(item.Arguments);
}
var sourceData = methodInfo.GetCustomAttributes<NUnit.Framework.TestCaseSourceAttribute>(true);
foreach (var item in sourceData)
{
var enumerator = GetTestCaseSource(methodInfo, item.SourceType, item.SourceName, item.MethodParams);
foreach (var item2 in enumerator)
{
var item3 = item2 as IEnumerable; // object[][]
if (item3 != null)
{
var l = new List<object>();
foreach (var item4 in item3)
{
l.Add(item4);
}
testCases.Add(l.ToArray());
}
}
}
return testCases;
}
IEnumerable GetTestCaseSource(MethodInfo method, Type sourceType, string sourceName, object[] methodParams)
{
Type type = sourceType ?? method.DeclaringType;
MemberInfo[] member = type.GetMember(sourceName, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
if (member.Length == 1)
{
MemberInfo memberInfo = member[0];
FieldInfo fieldInfo = memberInfo as FieldInfo;
if ((object)fieldInfo != null)
{
return (!fieldInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)fieldInfo.GetValue(null)) : ReturnErrorAsParameter("You have specified a data source field but also given a set of parameters. Fields cannot take parameters, please revise the 3rd parameter passed to the TestCaseSourceAttribute and either remove it or specify a method."));
}
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
if ((object)propertyInfo != null)
{
return (!propertyInfo.GetGetMethod(nonPublic: true).IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)propertyInfo.GetValue(null, null)) : ReturnErrorAsParameter("You have specified a data source property but also given a set of parameters. Properties cannot take parameters, please revise the 3rd parameter passed to the TestCaseSource attribute and either remove it or specify a method."));
}
MethodInfo methodInfo = memberInfo as MethodInfo;
if ((object)methodInfo != null)
{
return (!methodInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null || methodInfo.GetParameters().Length == methodParams.Length) ? ((IEnumerable)methodInfo.Invoke(null, methodParams)) : ReturnErrorAsParameter("You have given the wrong number of arguments to the method in the TestCaseSourceAttribute, please check the number of parameters passed in the object is correct in the 3rd parameter for the TestCaseSourceAttribute and this matches the number of parameters in the target method and try again."));
}
}
return null;
}
MethodInfo InferGenericType(MethodInfo methodInfo, object[] parameters)
{
var set = new HashSet<Type>();
List<Type> genericParameters = new List<Type>();
foreach (var item in methodInfo.GetParameters()
.Select((x, i) => new { x.ParameterType, i })
.Where(x => x.ParameterType.IsGenericParameter)
.OrderBy(x => x.ParameterType.GenericParameterPosition))
{
if (set.Add(item.ParameterType)) // DistinctBy
{
genericParameters.Add(parameters[item.i].GetType());
}
}
return methodInfo.MakeGenericMethod(genericParameters.ToArray());
}
IEnumerable ReturnErrorAsParameter(string name)
{
throw new Exception(name);
}
System.Collections.IEnumerator ScrollLogToEndNextFrame()
{
yield return null;
yield return null;
logScrollBar.value = 0;
}
IEnumerator RunTestInCoroutine(KeyValuePair<string, List<TestKeyValuePair>> actionList)
{
Button self = null;
foreach (var btn in list.GetComponentsInChildren<Button>())
{
btn.interactable = false;
if (btn.name == actionList.Key) self = btn;
}
if (self != null)
{
self.GetComponent<Image>().color = normalColor;
}
var allGreen = true;
AppendToGraphicText("<color=yellow>" + actionList.Key + "</color>\n");
WriteToConsole("Begin Test Class: " + actionList.Key);
yield return null;
var totalExecutionTime = new List<double>();
foreach (var item2 in actionList.Value)
{
// setup
try
{
foreach (var setup in item2.Setups)
{
setup();
}
// before start, cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
AppendToGraphicText("<color=teal>" + item2.Key + "</color>\n");
yield return null;
var v = item2.Value;
var methodStopwatch = System.Diagnostics.Stopwatch.StartNew();
Exception exception = null;
if (v is Action)
{
try
{
((Action)v).Invoke();
}
catch (Exception ex)
{
exception = ex;
}
}
else
{
var coroutineFactory = (Func<IEnumerator>)v;
IEnumerator coroutine = null;
try
{
coroutine = coroutineFactory();
}
catch (Exception ex)
{
exception = ex;
}
if (exception == null)
{
yield return StartCoroutine(UnwrapEnumerator(coroutine, ex =>
{
exception = ex;
}));
}
}
methodStopwatch.Stop();
totalExecutionTime.Add(methodStopwatch.Elapsed.TotalMilliseconds);
if (exception == null)
{
AppendToGraphicText("OK, " + methodStopwatch.Elapsed.TotalMilliseconds.ToString("0.00") + "ms\n");
WriteToConsoleResult(item2.Key + ", " + methodStopwatch.Elapsed.TotalMilliseconds.ToString("0.00") + "ms", true);
}
else
{
AppendToGraphicText("<color=red>" + exception.ToString() + "</color>\n");
WriteToConsoleResult(item2.Key + ", " + exception.ToString(), false);
allGreen = false;
allTestGreen = false;
}
}
finally
{
foreach (var teardown in item2.Teardowns)
{
teardown();
}
}
}
AppendToGraphicText("[" + actionList.Key + "]" + totalExecutionTime.Sum().ToString("0.00") + "ms\n\n");
foreach (var btn in list.GetComponentsInChildren<Button>()) btn.interactable = true;
if (self != null)
{
self.GetComponent<Image>().color = allGreen ? passColor : failColor;
}
yield return StartCoroutine(ScrollLogToEndNextFrame());
}
IEnumerator ExecuteAllInCoroutine(List<Func<Coroutine>> tests)
{
allTestGreen = true;
foreach (var item in tests)
{
yield return item();
}
if (Application.isBatchMode)
{
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
bool disableAutoClose = (scene.name.Contains("DisableAutoClose"));
if (allTestGreen)
{
WriteToConsole("Test Complete Successfully");
if (!disableAutoClose)
{
Application.Quit();
}
}
else
{
WriteToConsole("Test Failed, please see [NG] log.");
if (!disableAutoClose)
{
Application.Quit(1);
}
}
}
}
IEnumerator UnwrapEnumerator(IEnumerator enumerator, Action<Exception> exceptionCallback)
{
var hasNext = true;
while (hasNext)
{
try
{
hasNext = enumerator.MoveNext();
}
catch (Exception ex)
{
exceptionCallback(ex);
hasNext = false;
}
if (hasNext)
{
// unwrap self for bug of Unity
// https://issuetracker.unity3d.com/issues/does-not-stop-coroutine-when-it-throws-exception-in-movenext-at-first-frame
var moreCoroutine = enumerator.Current as IEnumerator;
if (moreCoroutine != null)
{
yield return StartCoroutine(UnwrapEnumerator(moreCoroutine, ex =>
{
exceptionCallback(ex);
hasNext = false;
}));
}
else
{
yield return enumerator.Current;
}
}
}
}
static void WriteToConsole(string msg)
{
if (Application.isBatchMode)
{
Console.WriteLine(msg);
}
}
void AppendToGraphicText(string msg)
{
if (!Application.isBatchMode)
{
if (logClear)
{
logText.text = "";
logClear = false;
}
try
{
logText.text += msg;
}
catch
{
logClear = true;
}
}
}
static void WriteToConsoleResult(string msg, bool green)
{
if (Application.isBatchMode)
{
if (!green)
{
var currentForeground = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("[NG]");
Console.ForegroundColor = currentForeground;
}
else
{
var currentForeground = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("[OK]");
Console.ForegroundColor = currentForeground;
}
System.Console.WriteLine(msg);
}
}
struct Pair
{
public string Name;
public UnityAction Action;
}
}
public class TestKeyValuePair
{
public string Key;
/// <summary>IEnumerator or Func[IEnumerator]</summary>
public object Value;
public List<Action> Setups;
public List<Action> Teardowns;
public TestKeyValuePair(string key, object value, List<Action> setups, List<Action> teardowns)
{
this.Key = key;
this.Value = value;
this.Setups = setups;
this.Teardowns = teardowns;
}
}
}

View File

@@ -1,11 +0,0 @@
{
"name": "com.cysharp.runtimeunittesttoolkit",
"displayName": "RuntimeUnitTestToolkit",
"version": "2.3.0",
"unity": "2018.3",
"description": "CLI/GUI Frontend of Unity Test Runner to test on any platform.",
"keywords": ["test"],
"category": "Tests",
"dependencies": {
}
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: b7883c7ac5d6ea4409a229aeab14e796
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -216,7 +216,7 @@
// RETRY: // RETRY:
// try // try
// { // {
// context.RequestHeaders["x-accesss-token"] = token; // context.RequestHeaders["x-access-token"] = token;
// return await next(context, cancellationToken); // return await next(context, cancellationToken);
// } // }
// catch (UnityWebRequestException ex) // catch (UnityWebRequestException ex)

View File

@@ -18,10 +18,14 @@ using UnityEngine.SceneManagement;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using System.IO; using System.IO;
using System.Linq.Expressions; using System.Linq.Expressions;
using UnityEngine.Events;
// using DG.Tweening; // using DG.Tweening;
public struct MyJob : IJob public struct MyJob : IJob
{ {
public int loopCount; public int loopCount;
@@ -116,7 +120,28 @@ public class AsyncMessageBroker<T> : IDisposable
connection.Dispose(); connection.Dispose();
} }
} }
public class WhenEachTest
{
public async UniTask Each()
{
var a = Delay(1, 3000);
var b = Delay(2, 1000);
var c = Delay(3, 2000);
var l = new List<int>();
await foreach (var item in UniTask.WhenEach(a, b, c))
{
Debug.Log(item.Result);
}
}
async UniTask<int> Delay(int id, int sleep)
{
await UniTask.Delay(sleep);
return id;
}
}
public class SandboxMain : MonoBehaviour public class SandboxMain : MonoBehaviour
{ {
@@ -144,6 +169,19 @@ public class SandboxMain : MonoBehaviour
Debug.Log("Again"); Debug.Log("Again");
// var foo = InstantiateAsync<SandboxMain>(this).ToUniTask();
// var tako = await foo;
//UnityAction action;
return 10; return 10;
} }
@@ -554,6 +592,7 @@ public class SandboxMain : MonoBehaviour
async UniTaskVoid Start() async UniTaskVoid Start()
{ {
await new WhenEachTest().Each();
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException // UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException

View File

@@ -13,7 +13,7 @@ OcclusionCullingSettings:
--- !u!104 &2 --- !u!104 &2
RenderSettings: RenderSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
serializedVersion: 9 serializedVersion: 10
m_Fog: 0 m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3 m_FogMode: 3
@@ -38,13 +38,11 @@ RenderSettings:
m_ReflectionIntensity: 1 m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0} m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0} m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0 m_UseRadianceAmbientProbe: 0
--- !u!157 &3 --- !u!157 &3
LightmapSettings: LightmapSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
serializedVersion: 11 serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings: m_GISettings:
serializedVersion: 2 serializedVersion: 2
m_BounceScale: 1 m_BounceScale: 1
@@ -67,9 +65,6 @@ LightmapSettings:
m_LightmapParameters: {fileID: 0} m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1 m_LightmapsBakeMode: 1
m_TextureCompression: 1 m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2 m_ReflectionCompression: 2
m_MixedBakeMode: 2 m_MixedBakeMode: 2
m_BakeBackend: 0 m_BakeBackend: 0
@@ -98,13 +93,14 @@ LightmapSettings:
m_TrainingDataDestination: TrainingData m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4 m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0} m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 1 m_LightingSettings: {fileID: 4890085278179872738, guid: 814185d368762ed45a2298d112780689,
type: 2}
--- !u!196 &4 --- !u!196 &4
NavMeshSettings: NavMeshSettings:
serializedVersion: 2 serializedVersion: 2
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
m_BuildSettings: m_BuildSettings:
serializedVersion: 2 serializedVersion: 3
agentTypeID: 0 agentTypeID: 0
agentRadius: 0.5 agentRadius: 0.5
agentHeight: 2 agentHeight: 2
@@ -117,7 +113,9 @@ NavMeshSettings:
cellSize: 0.16666667 cellSize: 0.16666667
manualTileSize: 0 manualTileSize: 0
tileSize: 256 tileSize: 256
accuratePlacement: 0 buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug: debug:
m_Flags: 0 m_Flags: 0
m_NavMeshData: {fileID: 0} m_NavMeshData: {fileID: 0}
@@ -150,10 +148,10 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 1584557232} - {fileID: 1584557232}
m_Father: {fileID: 1556045508} m_Father: {fileID: 1556045508}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5}
@@ -174,6 +172,7 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_Navigation: m_Navigation:
m_Mode: 3 m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0} m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0} m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0} m_SelectOnLeft: {fileID: 0}
@@ -218,6 +217,8 @@ MonoBehaviour:
m_Material: {fileID: 0} m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1} m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1 m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
@@ -270,9 +271,17 @@ Camera:
m_projectionMatrixMode: 1 m_projectionMatrixMode: 1
m_GateFitMode: 2 m_GateFitMode: 2
m_FOVAxisMode: 0 m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24} m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0} m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect: m_NormalizedViewPortRect:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
@@ -306,12 +315,13 @@ Transform:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 518730348} m_GameObject: {fileID: 518730348}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 488, y: 418, z: 0} m_LocalPosition: {x: 488, y: 418, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &519420028 --- !u!1 &519420028
GameObject: GameObject:
@@ -352,9 +362,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3} m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
camera: {fileID: 518730349} mycamera: {fileID: 0}
okButton: {fileID: 16537672} okButton: {fileID: 16537672}
cancelButton: {fileID: 628393011} cancelButton: {fileID: 628393011}
RP1:
latestValue: 0
text: {fileID: 2101290655} text: {fileID: 2101290655}
button: {fileID: 0} button: {fileID: 0}
--- !u!20 &519420031 --- !u!20 &519420031
@@ -371,9 +383,17 @@ Camera:
m_projectionMatrixMode: 1 m_projectionMatrixMode: 1
m_GateFitMode: 2 m_GateFitMode: 2
m_FOVAxisMode: 0 m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24} m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0} m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect: m_NormalizedViewPortRect:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
@@ -407,12 +427,13 @@ Transform:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028} m_GameObject: {fileID: 519420028}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &628393009 --- !u!1 &628393009
GameObject: GameObject:
@@ -443,10 +464,10 @@ RectTransform:
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 865871445} - {fileID: 865871445}
m_Father: {fileID: 1556045508} m_Father: {fileID: 1556045508}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5}
@@ -467,6 +488,7 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_Navigation: m_Navigation:
m_Mode: 3 m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0} m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0} m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0} m_SelectOnLeft: {fileID: 0}
@@ -511,6 +533,8 @@ MonoBehaviour:
m_Material: {fileID: 0} m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1} m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1 m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
@@ -560,9 +584,9 @@ RectTransform:
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 628393010} m_Father: {fileID: 628393010}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1}
@@ -584,6 +608,8 @@ MonoBehaviour:
m_Material: {fileID: 0} m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1 m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
@@ -639,6 +665,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_SendPointerHoverToParent: 1
m_HorizontalAxis: Horizontal m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical m_VerticalAxis: Vertical
m_SubmitButton: Submit m_SubmitButton: Submit
@@ -668,12 +695,13 @@ Transform:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 872009839} m_GameObject: {fileID: 872009839}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1556045504 --- !u!1 &1556045504
GameObject: GameObject:
@@ -733,6 +761,7 @@ MonoBehaviour:
m_FallbackScreenDPI: 96 m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96 m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1 m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!223 &1556045507 --- !u!223 &1556045507
Canvas: Canvas:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -750,7 +779,9 @@ Canvas:
m_OverrideSorting: 0 m_OverrideSorting: 0
m_OverridePixelPerfect: 0 m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0 m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0 m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0 m_SortingLayerID: 0
m_SortingOrder: 0 m_SortingOrder: 0
m_TargetDisplay: 0 m_TargetDisplay: 0
@@ -764,12 +795,12 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 16537671} - {fileID: 16537671}
- {fileID: 628393010} - {fileID: 628393010}
- {fileID: 2101290654} - {fileID: 2101290654}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
@@ -804,9 +835,9 @@ RectTransform:
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 16537671} m_Father: {fileID: 16537671}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1}
@@ -828,6 +859,8 @@ MonoBehaviour:
m_Material: {fileID: 0} m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1 m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
@@ -883,9 +916,9 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 1556045508} m_Father: {fileID: 1556045508}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5}
@@ -907,6 +940,8 @@ MonoBehaviour:
m_Material: {fileID: 0} m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1 m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
@@ -932,3 +967,11 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2101290653} m_GameObject: {fileID: 2101290653}
m_CullTransparentMesh: 0 m_CullTransparentMesh: 0
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 519420032}
- {fileID: 518730350}
- {fileID: 872009842}
- {fileID: 1556045508}

View File

@@ -0,0 +1,74 @@
using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
// https://github.com/Cysharp/UniTask/issues/617
public class WaitWhileTest : MonoBehaviour
{
private const float c_CallInterval = 0.3f;
private float m_JustBeforeCallTime;
private TaskObj m_TestObj;
// Start is called before the first frame update
void Start()
{
m_JustBeforeCallTime = Time.unscaledTime;
m_TestObj = new TaskObj();
// m_TestObj.Test(CancellationToken.None).Forget();
}
// Update is called once per frame
void Update()
{
if (Time.unscaledTime - m_JustBeforeCallTime > c_CallInterval)
{
m_JustBeforeCallTime = Time.unscaledTime;
m_TestObj.Test(CancellationToken.None).Forget();
}
}
}
public class TaskObj
{
private CancellationTokenSource m_CancelTokenSource;
private const float c_FinishElapsedTime = 0.1f;
private float m_StartTime;
public async UniTask Test(CancellationToken token)
{
try
{
CancelAndDisposeTokenSource();
m_CancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
m_StartTime = Time.unscaledTime;
await UniTask.WaitWhile(IsContinued, cancellationToken: m_CancelTokenSource.Token, cancelImmediately: true);
Debug.Log("Task Finished");
}
catch (OperationCanceledException)
{
Debug.LogWarning("Task Canceled");
}
finally
{
CancelAndDisposeTokenSource();
}
}
private void CancelAndDisposeTokenSource()
{
m_CancelTokenSource?.Cancel();
m_CancelTokenSource?.Dispose();
m_CancelTokenSource = null;
}
private bool IsContinued()
{
return Time.unscaledTime - m_StartTime > c_FinishElapsedTime;
}
}

View File

@@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 660baed073888b8438569f57e42679b2 guid: a478e5f6126dc184ca902adfb35401b4
timeCreated: 1476793308
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@@ -0,0 +1,90 @@
using System.Collections;
using System.Threading;
using Cysharp.Threading.Tasks;
using FluentAssertions;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.TestTools;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Cysharp.Threading.TasksTests
{
public class AsyncOperationTest
{
[UnityTest]
public IEnumerator ResourcesLoad_Completed() => UniTask.ToCoroutine(async () =>
{
var asyncOperation = Resources.LoadAsync<Texture2D>("sample_texture");
await asyncOperation.ToUniTask();
asyncOperation.isDone.Should().BeTrue();
asyncOperation.asset.GetType().Should().Be(typeof(Texture2D));
});
[UnityTest]
public IEnumerator ResourcesLoad_CancelOnPlayerLoop() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var task = Resources.LoadAsync<Texture>("sample_texture").ToUniTask(cancellationToken: cts.Token, cancelImmediately: false);
cts.Cancel();
task.Status.Should().Be(UniTaskStatus.Pending);
await UniTask.NextFrame();
task.Status.Should().Be(UniTaskStatus.Canceled);
});
[Test]
public void ResourcesLoad_CancelImmediately()
{
{
var cts = new CancellationTokenSource();
var task = Resources.LoadAsync<Texture>("sample_texture").ToUniTask(cancellationToken: cts.Token, cancelImmediately: true);
cts.Cancel();
task.Status.Should().Be(UniTaskStatus.Canceled);
}
}
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
[UnityTest]
public IEnumerator UnityWebRequest_Completed() => UniTask.ToCoroutine(async () =>
{
var filePath = System.IO.Path.Combine(Application.dataPath, "Tests", "Resources", "sample_texture.png");
var asyncOperation = UnityWebRequest.Get($"file://{filePath}").SendWebRequest();
await asyncOperation.ToUniTask();
asyncOperation.isDone.Should().BeTrue();
asyncOperation.webRequest.result.Should().Be(UnityWebRequest.Result.Success);
});
[UnityTest]
public IEnumerator UnityWebRequest_CancelOnPlayerLoop() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var filePath = System.IO.Path.Combine(Application.dataPath, "Tests", "Resources", "sample_texture.png");
var task = UnityWebRequest.Get($"file://{filePath}").SendWebRequest().ToUniTask(cancellationToken: cts.Token);
cts.Cancel();
task.Status.Should().Be(UniTaskStatus.Pending);
await UniTask.NextFrame();
task.Status.Should().Be(UniTaskStatus.Canceled);
});
[Test]
public void UnityWebRequest_CancelImmediately()
{
var cts = new CancellationTokenSource();
cts.Cancel();
var filePath = System.IO.Path.Combine(Application.dataPath, "Tests", "Resources", "sample_texture.png");
var task = UnityWebRequest.Get($"file://{filePath}").SendWebRequest().ToUniTask(cancellationToken: cts.Token, cancelImmediately: true);
task.Status.Should().Be(UniTaskStatus.Canceled);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 295d574a16494d6aa4d02fcb32179e39
timeCreated: 1698887128

View File

@@ -145,6 +145,11 @@ namespace Cysharp.Threading.TasksTests
public int MyProperty { get; set; } public int MyProperty { get; set; }
} }
class MyBooleanClass
{
public bool MyProperty { get; set; }
}
[UnityTest] [UnityTest]
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () => public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
{ {
@@ -159,6 +164,20 @@ namespace Cysharp.Threading.TasksTests
diff.Should().Be(11); diff.Should().Be(11);
}); });
[UnityTest]
public IEnumerator WaitUntilWithState() => UniTask.ToCoroutine(async () =>
{
var v = new MyBooleanClass { MyProperty = false };
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = true).Forget();
var startFrame = Time.frameCount;
await UniTask.WaitUntil(v, static v => v.MyProperty, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame;
diff.Should().Be(11);
});
[UnityTest] [UnityTest]
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () => public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
{ {
@@ -173,6 +192,20 @@ namespace Cysharp.Threading.TasksTests
diff.Should().Be(11); diff.Should().Be(11);
}); });
[UnityTest]
public IEnumerator WaitWhileWithState() => UniTask.ToCoroutine(async () =>
{
var v = new MyBooleanClass { MyProperty = true };
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = false).Forget();
var startFrame = Time.frameCount;
await UniTask.WaitWhile(v, static v => v.MyProperty, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame;
diff.Should().Be(11);
});
[UnityTest] [UnityTest]
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () => public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
{ {

View File

@@ -1,4 +1,6 @@
using Cysharp.Threading.Tasks; #pragma warning disable CS0618
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq; using Cysharp.Threading.Tasks.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 894f21dfce4e82343a91661e1ec1a455 guid: 8d82913edf6ac48aca30f66ae9ba42d6
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,215 @@
fileFormatVersion: 2
guid: 535006a83baed4ebda99d24a909a2efe
TextureImporter:
internalIDToNameTable:
- first:
213: -2664112245596591751
second: sample_texture_0
- first:
213: -4606777057269188692
second: sample_texture_1
- first:
213: 1950921086533113773
second: sample_texture_2
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: sample_texture_0
rect:
serializedVersion: 2
x: 0
y: 76
width: 243
height: 251
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 9796277170c270bd0800000000000000
internalID: -2664112245596591751
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: sample_texture_1
rect:
serializedVersion: 2
x: 227
y: 0
width: 190
height: 231
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: ca7fc069ca07110c0800000000000000
internalID: -4606777057269188692
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: sample_texture_2
rect:
serializedVersion: 2
x: 398
y: 87
width: 202
height: 188
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: da710ab4460131b10800000000000000
internalID: 1950921086533113773
vertices: []
indices:
edges: []
weights: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable:
sample_texture_0: -2664112245596591751
sample_texture_1: -4606777057269188692
sample_texture_2: 1950921086533113773
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,6 @@
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2) #pragma warning disable CS0618
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine; using UnityEngine;

View File

@@ -0,0 +1,45 @@
{
"dependencies": {
"com.cysharp.runtimeunittesttoolkit": "https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0",
"com.unity.collab-proxy": "2.4.3",
"com.unity.ide.rider": "3.0.31",
"com.unity.ide.visualstudio": "2.0.22",
"com.unity.ide.vscode": "1.2.5",
"com.unity.test-framework": "1.1.33",
"com.unity.textmeshpro": "3.0.6",
"com.unity.timeline": "1.7.6",
"com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.9",
"com.unity.ugui": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.cloth": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.physics2d": "1.0.0",
"com.unity.modules.screencapture": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.terrainphysics": "1.0.0",
"com.unity.modules.tilemap": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.uielements": "1.0.0",
"com.unity.modules.umbra": "1.0.0",
"com.unity.modules.unityanalytics": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.unitywebrequesttexture": "1.0.0",
"com.unity.modules.unitywebrequestwww": "1.0.0",
"com.unity.modules.vehicles": "1.0.0",
"com.unity.modules.video": "1.0.0",
"com.unity.modules.vr": "1.0.0",
"com.unity.modules.wind": "1.0.0",
"com.unity.modules.xr": "1.0.0"
}
}

View File

@@ -0,0 +1,360 @@
{
"dependencies": {
"com.cysharp.runtimeunittesttoolkit": {
"version": "https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "4e3dbfaa9c40b5cfdcb71a1d4e8bca0d45ca1055"
},
"com.unity.collab-proxy": {
"version": "2.4.3",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.ext.nunit": {
"version": "1.0.6",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.31",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.ext.nunit": "1.0.6"
},
"url": "https://packages.unity.com"
},
"com.unity.ide.visualstudio": {
"version": "2.0.22",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.test-framework": "1.1.9"
},
"url": "https://packages.unity.com"
},
"com.unity.ide.vscode": {
"version": "1.2.5",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.sysroot": {
"version": "2.0.10",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.sysroot.linux-x86_64": {
"version": "2.0.9",
"depth": 1,
"source": "registry",
"dependencies": {
"com.unity.sysroot": "2.0.10"
},
"url": "https://packages.unity.com"
},
"com.unity.test-framework": {
"version": "1.1.33",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.ext.nunit": "1.0.6",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.textmeshpro": {
"version": "3.0.6",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.timeline": {
"version": "1.7.6",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.director": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.toolchain.win-x86_64-linux-x86_64": {
"version": "2.0.9",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.sysroot": "2.0.10",
"com.unity.sysroot.linux-x86_64": "2.0.9"
},
"url": "https://packages.unity.com"
},
"com.unity.ugui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"com.unity.modules.ai": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.androidjni": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.animation": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.assetbundle": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.audio": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.cloth": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0"
}
},
"com.unity.modules.director": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.animation": "1.0.0"
}
},
"com.unity.modules.imageconversion": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imgui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.jsonserialize": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.particlesystem": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.physics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.physics2d": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.screencapture": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.subsystems": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.modules.terrain": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.terrainphysics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.terrain": "1.0.0"
}
},
"com.unity.modules.tilemap": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics2d": "1.0.0"
}
},
"com.unity.modules.ui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.uielements": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.modules.umbra": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.unityanalytics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.modules.unitywebrequest": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.unitywebrequestassetbundle": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0"
}
},
"com.unity.modules.unitywebrequestaudio": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.audio": "1.0.0"
}
},
"com.unity.modules.unitywebrequesttexture": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.unitywebrequestwww": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.vehicles": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0"
}
},
"com.unity.modules.video": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0"
}
},
"com.unity.modules.vr": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.xr": "1.0.0"
}
},
"com.unity.modules.wind": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.xr": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.subsystems": "1.0.0"
}
}
}
}

View File

@@ -0,0 +1,35 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!387306366 &1
MemorySettings:
m_ObjectHideFlags: 0
m_EditorMemorySettings:
m_MainAllocatorBlockSize: -1
m_ThreadAllocatorBlockSize: -1
m_MainGfxBlockSize: -1
m_ThreadGfxBlockSize: -1
m_CacheBlockSize: -1
m_TypetreeBlockSize: -1
m_ProfilerBlockSize: -1
m_ProfilerEditorBlockSize: -1
m_BucketAllocatorGranularity: -1
m_BucketAllocatorBucketsCount: -1
m_BucketAllocatorBlockSize: -1
m_BucketAllocatorBlockCount: -1
m_ProfilerBucketAllocatorGranularity: -1
m_ProfilerBucketAllocatorBucketsCount: -1
m_ProfilerBucketAllocatorBlockSize: -1
m_ProfilerBucketAllocatorBlockCount: -1
m_TempAllocatorSizeMain: -1
m_JobTempAllocatorBlockSize: -1
m_BackgroundJobTempAllocatorBlockSize: -1
m_JobTempAllocatorReducedBlockSize: -1
m_TempAllocatorSizeGIBakingWorker: -1
m_TempAllocatorSizeNavMeshWorker: -1
m_TempAllocatorSizeAudioWorker: -1
m_TempAllocatorSizeCloudWorker: -1
m_TempAllocatorSizeGfx: -1
m_TempAllocatorSizeJobWorker: -1
m_TempAllocatorSizeBackgroundWorker: -1
m_TempAllocatorSizePreloadManager: -1
m_PlatformMemorySettings: {}

View File

@@ -3,7 +3,7 @@
--- !u!129 &1 --- !u!129 &1
PlayerSettings: PlayerSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
serializedVersion: 20 serializedVersion: 26
productGUID: 904cd7a3163037f42a9204c0e2f2b7bd productGUID: 904cd7a3163037f42a9204c0e2f2b7bd
AndroidProfiler: 0 AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0 AndroidFilterTouchesWhenObscured: 0
@@ -48,12 +48,16 @@ PlayerSettings:
defaultScreenHeightWeb: 600 defaultScreenHeightWeb: 600
m_StereoRenderingPath: 0 m_StereoRenderingPath: 0
m_ActiveColorSpace: 0 m_ActiveColorSpace: 0
unsupportedMSAAFallback: 0
m_SpriteBatchVertexThreshold: 300
m_MTRendering: 1 m_MTRendering: 1
mipStripping: 0
numberOfMipsStripped: 0
numberOfMipsStrippedPerMipmapLimitGroup: {}
m_StackTraceTypes: 010000000100000001000000010000000100000001000000 m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1 iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1
iosUseCustomAppBackgroundBehavior: 0 iosUseCustomAppBackgroundBehavior: 0
iosAllowHTTPDownload: 1
allowedAutorotateToPortrait: 1 allowedAutorotateToPortrait: 1
allowedAutorotateToPortraitUpsideDown: 1 allowedAutorotateToPortraitUpsideDown: 1
allowedAutorotateToLandscapeRight: 1 allowedAutorotateToLandscapeRight: 1
@@ -66,6 +70,13 @@ PlayerSettings:
androidRenderOutsideSafeArea: 1 androidRenderOutsideSafeArea: 1
androidUseSwappy: 0 androidUseSwappy: 0
androidBlitType: 0 androidBlitType: 0
androidResizableWindow: 0
androidDefaultWindowWidth: 1920
androidDefaultWindowHeight: 1080
androidMinimumWindowWidth: 400
androidMinimumWindowHeight: 300
androidFullscreenMode: 1
androidAutoRotationBehavior: 1
defaultIsNativeResolution: 1 defaultIsNativeResolution: 1
macRetinaSupport: 1 macRetinaSupport: 1
runInBackground: 1 runInBackground: 1
@@ -77,6 +88,7 @@ PlayerSettings:
hideHomeButton: 0 hideHomeButton: 0
submitAnalytics: 1 submitAnalytics: 1
usePlayerLog: 1 usePlayerLog: 1
dedicatedServerOptimizations: 0
bakeCollisionMeshes: 0 bakeCollisionMeshes: 0
forceSingleInstance: 0 forceSingleInstance: 0
useFlipModelSwapchain: 1 useFlipModelSwapchain: 1
@@ -111,16 +123,22 @@ PlayerSettings:
switchNVNShaderPoolsGranularity: 33554432 switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216 switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216
switchGpuScratchPoolGranularity: 2097152
switchAllowGpuScratchShrinking: 0
switchNVNMaxPublicTextureIDCount: 0
switchNVNMaxPublicSamplerIDCount: 0
switchNVNGraphicsFirmwareMemory: 32
switchMaxWorkerMultiple: 8
stadiaPresentMode: 0 stadiaPresentMode: 0
stadiaTargetFramerate: 0 stadiaTargetFramerate: 0
vulkanNumSwapchainBuffers: 3 vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0 vulkanEnableSetSRGBWrite: 0
m_SupportedAspectRatios: vulkanEnablePreTransform: 0
4:3: 1 vulkanEnableLateAcquireNextImage: 0
5:4: 1 vulkanEnableCommandBufferRecycling: 1
16:10: 1 loadStoreDebugModeEnabled: 0
16:9: 1 visionOSBundleVersion: 1.0
Others: 1 tvOSBundleVersion: 1.0
bundleVersion: 0.1 bundleVersion: 0.1
preloadedAssets: [] preloadedAssets: []
metroInputSource: 0 metroInputSource: 0
@@ -129,46 +147,29 @@ PlayerSettings:
xboxOneDisableKinectGpuReservation: 1 xboxOneDisableKinectGpuReservation: 1
xboxOneEnable7thCore: 1 xboxOneEnable7thCore: 1
vrSettings: vrSettings:
cardboard:
depthFormat: 0
enableTransitionView: 0
daydream:
depthFormat: 0
useSustainedPerformanceMode: 0
enableVideoLayer: 0
useProtectedVideoMemory: 0
minimumSupportedHeadTracking: 0
maximumSupportedHeadTracking: 1
hololens:
depthFormat: 1
depthBufferSharingEnabled: 1
lumin:
depthFormat: 0
frameTiming: 2
enableGLCache: 0
glCacheMaxBlobSize: 524288
glCacheMaxFileSize: 8388608
oculus:
sharedDepthBuffer: 1
dashSupport: 1
lowOverheadMode: 0
protectedContext: 0
v2Signing: 1
enable360StereoCapture: 0 enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0 isWsaHolographicRemotingEnabled: 0
enableFrameTimingStats: 0 enableFrameTimingStats: 0
enableOpenGLProfilerGPURecorders: 1
allowHDRDisplaySupport: 0
useHDRDisplay: 0 useHDRDisplay: 0
D3DHDRBitDepth: 0 hdrBitDepth: 0
m_ColorGamuts: 00000000 m_ColorGamuts: 00000000
targetPixelDensity: 30 targetPixelDensity: 30
resolutionScalingMode: 0 resolutionScalingMode: 0
resetResolutionOnWindowResize: 0
androidSupportedAspectRatio: 1 androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1 androidMaxAspectRatio: 2.1
applicationIdentifier: applicationIdentifier:
Standalone: com.Company.ProductName Standalone: com.DefaultCompany.UniTask
buildNumber: {} buildNumber:
Standalone: 0
VisionOS: 0
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 0
AndroidBundleVersionCode: 1 AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 19 AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 0 AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1 AndroidPreferredInstallLocation: 1
aotOptions: aotOptions:
@@ -181,12 +182,15 @@ PlayerSettings:
APKExpansionFiles: 0 APKExpansionFiles: 0
keepLoadedShadersAlive: 0 keepLoadedShadersAlive: 0
StripUnusedMeshComponents: 1 StripUnusedMeshComponents: 1
strictShaderVariantMatching: 0
VertexChannelCompressionMask: 4054 VertexChannelCompressionMask: 4054
iPhoneSdkVersion: 988 iPhoneSdkVersion: 988
iOSTargetOSVersionString: 10.0 iOSTargetOSVersionString: 12.0
tvOSSdkVersion: 0 tvOSSdkVersion: 0
tvOSRequireExtendedGameController: 0 tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: 10.0 tvOSTargetOSVersionString: 12.0
VisionOSSdkVersion: 0
VisionOSTargetOSVersionString: 1.0
uIPrerenderedIcon: 0 uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0 uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1 uIRequiresFullScreen: 1
@@ -220,32 +224,48 @@ PlayerSettings:
iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadFillPct: 100
iOSLaunchScreeniPadSize: 100 iOSLaunchScreeniPadSize: 100
iOSLaunchScreeniPadCustomXibPath: iOSLaunchScreeniPadCustomXibPath:
iOSUseLaunchScreenStoryboard: 0
iOSLaunchScreenCustomStoryboardPath: iOSLaunchScreenCustomStoryboardPath:
iOSLaunchScreeniPadCustomStoryboardPath:
iOSDeviceRequirements: [] iOSDeviceRequirements: []
iOSURLSchemes: [] iOSURLSchemes: []
macOSURLSchemes: []
iOSBackgroundModes: 0 iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0 iOSMetalForceHardShadows: 0
metalEditorSupport: 1 metalEditorSupport: 1
metalAPIValidation: 1 metalAPIValidation: 1
metalCompileShaderBinary: 0
iOSRenderExtraFrameOnPause: 0 iOSRenderExtraFrameOnPause: 0
iosCopyPluginsCodeInsteadOfSymlink: 0
appleDeveloperTeamID: appleDeveloperTeamID:
iOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileID:
tvOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID:
VisionOSManualSigningProvisioningProfileID:
iOSManualSigningProvisioningProfileType: 0 iOSManualSigningProvisioningProfileType: 0
tvOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0
VisionOSManualSigningProvisioningProfileType: 0
appleEnableAutomaticSigning: 0 appleEnableAutomaticSigning: 0
iOSRequireARKit: 0 iOSRequireARKit: 0
iOSAutomaticallyDetectAndAddCapabilities: 1 iOSAutomaticallyDetectAndAddCapabilities: 1
appleEnableProMotion: 0 appleEnableProMotion: 0
shaderPrecisionModel: 0
clonedFromGUID: 5f34be1353de5cf4398729fda238591b clonedFromGUID: 5f34be1353de5cf4398729fda238591b
templatePackageId: com.unity.template.2d@3.1.0 templatePackageId: com.unity.template.2d@3.1.0
templateDefaultScene: Assets/Scenes/SampleScene.unity templateDefaultScene: Assets/Scenes/SampleScene.unity
useCustomMainManifest: 0
useCustomLauncherManifest: 0
useCustomMainGradleTemplate: 0
useCustomLauncherGradleManifest: 0
useCustomBaseGradleTemplate: 0
useCustomGradlePropertiesTemplate: 0
useCustomGradleSettingsTemplate: 0
useCustomProguardFile: 0
AndroidTargetArchitectures: 1 AndroidTargetArchitectures: 1
AndroidTargetDevices: 0
AndroidSplashScreenScale: 0 AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0} androidSplashScreen: {fileID: 0}
AndroidKeystoreName: '{inproject}: ' AndroidKeystoreName: '{inproject}: '
AndroidKeyaliasName: AndroidKeyaliasName:
AndroidEnableArmv9SecurityFeatures: 0
AndroidBuildApkPerCpuArchitecture: 0 AndroidBuildApkPerCpuArchitecture: 0
AndroidTVCompatibility: 0 AndroidTVCompatibility: 0
AndroidIsGame: 1 AndroidIsGame: 1
@@ -258,11 +278,15 @@ PlayerSettings:
height: 180 height: 180
banner: {fileID: 0} banner: {fileID: 0}
androidGamepadSupportLevel: 0 androidGamepadSupportLevel: 0
chromeosInputEmulation: 1
AndroidMinifyRelease: 0
AndroidMinifyDebug: 0
AndroidValidateAppBundleSize: 1 AndroidValidateAppBundleSize: 1
AndroidAppBundleSizeToValidate: 100 AndroidAppBundleSizeToValidate: 100
m_BuildTargetIcons: [] m_BuildTargetIcons: []
m_BuildTargetPlatformIcons: [] m_BuildTargetPlatformIcons: []
m_BuildTargetBatching: [] m_BuildTargetBatching: []
m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs: m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport - m_BuildTarget: MacStandaloneSupport
m_GraphicsJobs: 0 m_GraphicsJobs: 0
@@ -298,8 +322,13 @@ PlayerSettings:
m_BuildTargetGraphicsAPIs: m_BuildTargetGraphicsAPIs:
- m_BuildTarget: AndroidPlayer - m_BuildTarget: AndroidPlayer
m_APIs: 150000000b000000 m_APIs: 150000000b000000
m_Automatic: 0 m_Automatic: 1
- m_BuildTarget: iOSSupport
m_APIs: 10000000
m_Automatic: 1
m_BuildTargetVRSettings: [] m_BuildTargetVRSettings: []
m_DefaultShaderChunkSizeInMB: 16
m_DefaultShaderChunkCount: 0
openGLRequireES31: 0 openGLRequireES31: 0
openGLRequireES31AEP: 0 openGLRequireES31AEP: 0
openGLRequireES32: 0 openGLRequireES32: 0
@@ -309,7 +338,11 @@ PlayerSettings:
iPhone: 1 iPhone: 1
tvOS: 1 tvOS: 1
m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapEncodingQuality: []
m_BuildTargetGroupHDRCubemapEncodingQuality: []
m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLightmapSettings: []
m_BuildTargetGroupLoadStoreDebugModeSettings: []
m_BuildTargetNormalMapEncoding: []
m_BuildTargetDefaultTextureCompressionFormat: []
playModeTestRunnerEnabled: 0 playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0 runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1 actionOnDotNetUnhandledException: 1
@@ -319,14 +352,20 @@ PlayerSettings:
cameraUsageDescription: cameraUsageDescription:
locationUsageDescription: locationUsageDescription:
microphoneUsageDescription: microphoneUsageDescription:
bluetoothUsageDescription:
macOSTargetOSVersion: 10.13.0
switchNMETAOverride:
switchNetLibKey: switchNetLibKey:
switchSocketMemoryPoolSize: 6144 switchSocketMemoryPoolSize: 6144
switchSocketAllocatorPoolSize: 128 switchSocketAllocatorPoolSize: 128
switchSocketConcurrencyLimit: 14 switchSocketConcurrencyLimit: 14
switchScreenResolutionBehavior: 2 switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0 switchUseCPUProfiler: 0
switchEnableFileSystemTrace: 0
switchLTOSetting: 0
switchApplicationID: 0x01004b9000490000 switchApplicationID: 0x01004b9000490000
switchNSODependencies: switchNSODependencies:
switchCompilerFlags:
switchTitleNames_0: switchTitleNames_0:
switchTitleNames_1: switchTitleNames_1:
switchTitleNames_2: switchTitleNames_2:
@@ -342,6 +381,7 @@ PlayerSettings:
switchTitleNames_12: switchTitleNames_12:
switchTitleNames_13: switchTitleNames_13:
switchTitleNames_14: switchTitleNames_14:
switchTitleNames_15:
switchPublisherNames_0: switchPublisherNames_0:
switchPublisherNames_1: switchPublisherNames_1:
switchPublisherNames_2: switchPublisherNames_2:
@@ -357,6 +397,7 @@ PlayerSettings:
switchPublisherNames_12: switchPublisherNames_12:
switchPublisherNames_13: switchPublisherNames_13:
switchPublisherNames_14: switchPublisherNames_14:
switchPublisherNames_15:
switchIcons_0: {fileID: 0} switchIcons_0: {fileID: 0}
switchIcons_1: {fileID: 0} switchIcons_1: {fileID: 0}
switchIcons_2: {fileID: 0} switchIcons_2: {fileID: 0}
@@ -372,6 +413,7 @@ PlayerSettings:
switchIcons_12: {fileID: 0} switchIcons_12: {fileID: 0}
switchIcons_13: {fileID: 0} switchIcons_13: {fileID: 0}
switchIcons_14: {fileID: 0} switchIcons_14: {fileID: 0}
switchIcons_15: {fileID: 0}
switchSmallIcons_0: {fileID: 0} switchSmallIcons_0: {fileID: 0}
switchSmallIcons_1: {fileID: 0} switchSmallIcons_1: {fileID: 0}
switchSmallIcons_2: {fileID: 0} switchSmallIcons_2: {fileID: 0}
@@ -387,6 +429,7 @@ PlayerSettings:
switchSmallIcons_12: {fileID: 0} switchSmallIcons_12: {fileID: 0}
switchSmallIcons_13: {fileID: 0} switchSmallIcons_13: {fileID: 0}
switchSmallIcons_14: {fileID: 0} switchSmallIcons_14: {fileID: 0}
switchSmallIcons_15: {fileID: 0}
switchManualHTML: switchManualHTML:
switchAccessibleURLs: switchAccessibleURLs:
switchLegalInformation: switchLegalInformation:
@@ -396,7 +439,6 @@ PlayerSettings:
switchReleaseVersion: 0 switchReleaseVersion: 0
switchDisplayVersion: 1.0.0 switchDisplayVersion: 1.0.0
switchStartupUserAccount: 0 switchStartupUserAccount: 0
switchTouchScreenUsage: 0
switchSupportedLanguagesMask: 0 switchSupportedLanguagesMask: 0
switchLogoType: 0 switchLogoType: 0
switchApplicationErrorCodeCategory: switchApplicationErrorCodeCategory:
@@ -438,6 +480,7 @@ PlayerSettings:
switchNativeFsCacheSize: 32 switchNativeFsCacheSize: 32
switchIsHoldTypeHorizontal: 0 switchIsHoldTypeHorizontal: 0
switchSupportedNpadCount: 8 switchSupportedNpadCount: 8
switchEnableTouchScreen: 1
switchSocketConfigEnabled: 0 switchSocketConfigEnabled: 0
switchTcpInitialSendBufferSize: 32 switchTcpInitialSendBufferSize: 32
switchTcpInitialReceiveBufferSize: 64 switchTcpInitialReceiveBufferSize: 64
@@ -448,7 +491,12 @@ PlayerSettings:
switchSocketBufferEfficiency: 4 switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1 switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1
switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0
switchUseLegacyFmodPriorities: 0
switchUseMicroSleepForYield: 1
switchEnableRamDiskSupport: 0
switchMicroSleepForYieldTime: 25
switchRamDiskSpaceSize: 12
ps4NPAgeRating: 12 ps4NPAgeRating: 12
ps4NPTitleSecret: ps4NPTitleSecret:
ps4NPTrophyPackPath: ps4NPTrophyPackPath:
@@ -475,6 +523,7 @@ PlayerSettings:
ps4ShareFilePath: ps4ShareFilePath:
ps4ShareOverlayImagePath: ps4ShareOverlayImagePath:
ps4PrivacyGuardImagePath: ps4PrivacyGuardImagePath:
ps4ExtraSceSysFile:
ps4NPtitleDatPath: ps4NPtitleDatPath:
ps4RemotePlayKeyAssignment: -1 ps4RemotePlayKeyAssignment: -1
ps4RemotePlayKeyMappingDir: ps4RemotePlayKeyMappingDir:
@@ -517,6 +566,9 @@ PlayerSettings:
ps4disableAutoHideSplash: 0 ps4disableAutoHideSplash: 0
ps4videoRecordingFeaturesUsed: 0 ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0
ps4CompatibilityPS5: 0
ps4AllowPS5Detection: 0
ps4GPU800MHz: 1
ps4attribEyeToEyeDistanceSettingVR: 0 ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: [] ps4IncludedModules: []
ps4attribVROutputEnabled: 0 ps4attribVROutputEnabled: 0
@@ -528,6 +580,7 @@ PlayerSettings:
webGLMemorySize: 16 webGLMemorySize: 16
webGLExceptionSupport: 1 webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0 webGLNameFilesAsHashes: 0
webGLShowDiagnostics: 0
webGLDataCaching: 1 webGLDataCaching: 1
webGLDebugSymbols: 0 webGLDebugSymbols: 0
webGLEmscriptenArgs: webGLEmscriptenArgs:
@@ -536,18 +589,47 @@ PlayerSettings:
webGLAnalyzeBuildSize: 0 webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0 webGLUseEmbeddedResources: 0
webGLCompressionFormat: 1 webGLCompressionFormat: 1
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1 webGLLinkerTarget: 1
webGLThreadsSupport: 0 webGLThreadsSupport: 0
webGLWasmStreaming: 0 webGLDecompressionFallback: 0
webGLInitialMemorySize: 32
webGLMaximumMemorySize: 2048
webGLMemoryGrowthMode: 2
webGLMemoryLinearGrowthStep: 16
webGLMemoryGeometricGrowthStep: 0.2
webGLMemoryGeometricGrowthCap: 96
webGLPowerPreference: 2
scriptingDefineSymbols: {} scriptingDefineSymbols: {}
additionalCompilerArguments: {}
platformArchitecture: {} platformArchitecture: {}
scriptingBackend: scriptingBackend:
Android: 1 Android: 1
Server: 1
Standalone: 1 Standalone: 1
il2cppCompilerConfiguration: {} il2cppCompilerConfiguration: {}
managedStrippingLevel: {} il2cppCodeGeneration: {}
managedStrippingLevel:
Android: 1
EmbeddedLinux: 1
GameCoreScarlett: 1
GameCoreXboxOne: 1
Nintendo Switch: 1
PS4: 1
PS5: 1
QNX: 1
Stadia: 1
Standalone: 1
VisionOS: 1
WebGL: 1
Windows Store Apps: 1
XboxOne: 1
iPhone: 1
tvOS: 1
incrementalIl2cppBuild: {} incrementalIl2cppBuild: {}
suppressCommonWarnings: 1
allowUnsafeCode: 0 allowUnsafeCode: 0
useDeterministicCompilation: 1
additionalIl2CppArgs: additionalIl2CppArgs:
scriptingRuntimeVersion: 1 scriptingRuntimeVersion: 1
gcIncremental: 0 gcIncremental: 0
@@ -578,11 +660,13 @@ PlayerSettings:
metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,
a: 1} a: 1}
metroSplashScreenUseBackgroundColor: 0 metroSplashScreenUseBackgroundColor: 0
syncCapabilities: 0
platformCapabilities: {} platformCapabilities: {}
metroTargetDeviceFamilies: {} metroTargetDeviceFamilies: {}
metroFTAName: metroFTAName:
metroFTAFileTypes: [] metroFTAFileTypes: []
metroProtocolName: metroProtocolName:
vcxProjDefaultLanguage:
XboxOneProductId: XboxOneProductId:
XboxOneUpdateKey: XboxOneUpdateKey:
XboxOneSandboxId: XboxOneSandboxId:
@@ -601,6 +685,7 @@ PlayerSettings:
XboxOneCapability: [] XboxOneCapability: []
XboxOneGameRating: {} XboxOneGameRating: {}
XboxOneIsContentPackage: 0 XboxOneIsContentPackage: 0
XboxOneEnhancedXboxCompatibilityMode: 0
XboxOneEnableGPUVariability: 1 XboxOneEnableGPUVariability: 1
XboxOneSockets: {} XboxOneSockets: {}
XboxOneSplashScreen: {fileID: 0} XboxOneSplashScreen: {fileID: 0}
@@ -609,10 +694,7 @@ PlayerSettings:
XboxOneXTitleMemory: 8 XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName: XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher: XboxOneOverrideIdentityPublisher:
vrEditorSettings: vrEditorSettings: {}
daydream:
daydreamIconForeground: {fileID: 0}
daydreamIconBackground: {fileID: 0}
cloudServicesEnabled: cloudServicesEnabled:
UNet: 1 UNet: 1
luminIcon: luminIcon:
@@ -626,12 +708,22 @@ PlayerSettings:
luminVersion: luminVersion:
m_VersionCode: 1 m_VersionCode: 1
m_VersionName: m_VersionName:
hmiPlayerDataPath:
hmiForceSRGBBlit: 1
embeddedLinuxEnableGamepadInput: 1
hmiLogStartupTiming: 0
hmiCpuConfiguration:
apiCompatibilityLevel: 6 apiCompatibilityLevel: 6
activeInputHandler: 0
windowsGamepadBackendHint: 0
cloudProjectId: cloudProjectId:
framebufferDepthMemorylessMode: 0 framebufferDepthMemorylessMode: 0
qualitySettingsNames: []
projectName: projectName:
organizationId: organizationId:
cloudEnabled: 0 cloudEnabled: 0
enableNativePlatformBackendsForNewInputSystem: 0
disableOldInputManagerSupport: 0
legacyClampBlendShapeWeights: 1 legacyClampBlendShapeWeights: 1
hmiLoadingImage: {fileID: 0}
platformRequiresReadableAssets: 0
virtualTexturingSupportEnabled: 0
insecureHttpOption: 0

View File

@@ -1,2 +1,2 @@
m_EditorVersion: 2020.2.1f1 m_EditorVersion: 2022.3.39f1
m_EditorVersionWithRevision: 2020.2.1f1 (270dd8c3da1c) m_EditorVersionWithRevision: 2022.3.39f1 (4e1b0f82c39a)

View File

@@ -0,0 +1,121 @@
{
"templatePinStates": [],
"dependencyTypeInfos": [
{
"userAdded": false,
"type": "UnityEngine.AnimationClip",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.Animations.AnimatorController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.AnimatorOverrideController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.Audio.AudioMixerController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.ComputeShader",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Cubemap",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.GameObject",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.LightingDataAsset",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.LightingSettings",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Material",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.MonoScript",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.PhysicMaterial",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.PhysicsMaterial2D",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.PostProcessing.PostProcessResources",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.VolumeProfile",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.SceneAsset",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Shader",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.ShaderVariantCollection",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Texture",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Texture2D",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Timeline.TimelineAsset",
"defaultInstantiationMode": 0
}
],
"defaultDependencyTypeInfo": {
"userAdded": false,
"type": "<default_scene_template_dependencies>",
"defaultInstantiationMode": 1
},
"newSceneOverride": 0
}

View File

@@ -9,6 +9,7 @@ UnityConnectSettings:
m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events
m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events
m_ConfigUrl: https://config.uca.cloud.unity3d.com m_ConfigUrl: https://config.uca.cloud.unity3d.com
m_DashboardUrl: https://dashboard.unity3d.com
m_TestInitMode: 0 m_TestInitMode: 0
CrashReportingSettings: CrashReportingSettings:
m_EventUrl: https://perf-events.cloud.unity3d.com m_EventUrl: https://perf-events.cloud.unity3d.com
@@ -22,6 +23,7 @@ UnityConnectSettings:
m_Enabled: 0 m_Enabled: 0
m_TestMode: 0 m_TestMode: 0
m_InitializeOnStartup: 1 m_InitializeOnStartup: 1
m_PackageRequiringCoreStatsPresent: 0
UnityAdsSettings: UnityAdsSettings:
m_Enabled: 0 m_Enabled: 0
m_InitializeOnStartup: 1 m_InitializeOnStartup: 1

View File

@@ -1,24 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!162 &1
EditorUserSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_ConfigSettings:
RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c1118131a25340527392367083debf42d
flags: 0
vcSharedLogLevel:
value: 0d5e400f0650
flags: 0
m_VCAutomaticAdd: 1
m_VCDebugCom: 0
m_VCDebugCmd: 0
m_VCDebugOut: 0
m_SemanticMergeMode: 2
m_VCShowFailedCheckout: 1
m_VCOverwriteFailedCheckoutAssets: 1
m_VCProjectOverlayIcons: 1
m_VCHierarchyOverlayIcons: 1
m_VCOtherOverlayIcons: 1
m_VCAllowAsyncUpdate: 0