From 75c325c4cc777bbc340f9f5261bb729d1665d559 Mon Sep 17 00:00:00 2001 From: Maxime Date: Wed, 22 Nov 2023 20:18:32 +0100 Subject: [PATCH] Spline movement works for walking and falling --- Pawn_Unreal/Content/Characters/BP_Judy.uasset | 4 +- Pawn_Unreal/Content/Input/IMC_Judy.uasset | 4 +- .../0/91/BERD0JFDCI5RAYAPS4DY1P.uasset | 4 +- .../0/XC/EES9G2P00B5EO5FPWE8N4U.uasset | 4 +- .../1/LE/RIS6ZA9R1KK7MS3EGVDKZV.uasset | 4 +- .../1/ZO/2T6IY0BJD55BZXPJC2K1VW.uasset | 4 +- .../3/7A/SCU1D1LU7J4NS7X4MENA7G.uasset | 3 + .../3/S1/Q0FDQMTTCM0M6RLWXOTPMF.uasset | 2 +- .../3/ZC/SPZ5ZSIG7BW3194FWIBEWC.uasset | 4 +- .../3/ZX/V7QFRP3DMWRT8NZCJQYVRB.uasset | 3 + .../4/OH/3YZOO5HQPTMDBNJPPM4RV0.uasset | 4 +- .../5/V3/7TDZ89SOYDGI26YWXQ04D4.uasset | 3 + .../9/IH/5T09JNLLSNG3U1KPWNFK3T.uasset | 3 + .../9/RQ/JYNZEWOG6SXNE3HGUCSZIE.uasset | 4 +- .../A/BZ/XZUBN8NU0UZ2W8T2MBWCYN.uasset | 4 +- .../A/U2/4ECO0ADAHJEURYSR8OYVEX.uasset | 4 +- .../B/0Z/X5N2DTGOJMHQWHOBBKFNDX.uasset | 3 + .../C/6U/C2O2TW7RAVDUQGZS7QLKMX.uasset | 3 + .../C/BU/19EKBOJAPNXX8XDUPL0Z18.uasset | 2 +- .../D/VD/LJAQY01SJR93NPW0FPCPIL.uasset | 4 +- .../E/86/NKWQISSIQGRX07O716DOXO.uasset | 3 + .../PwnCharacterMovementComponent.cpp | 462 ++++++++++++++++-- .../PwnCharacterMovementComponent.h | 27 +- .../Source/Pawn/Public/Utils/EngineUtils.h | 3 + 24 files changed, 485 insertions(+), 80 deletions(-) create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/7A/SCU1D1LU7J4NS7X4MENA7G.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZX/V7QFRP3DMWRT8NZCJQYVRB.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/5/V3/7TDZ89SOYDGI26YWXQ04D4.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/IH/5T09JNLLSNG3U1KPWNFK3T.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/B/0Z/X5N2DTGOJMHQWHOBBKFNDX.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/6U/C2O2TW7RAVDUQGZS7QLKMX.uasset create mode 100644 Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/E/86/NKWQISSIQGRX07O716DOXO.uasset diff --git a/Pawn_Unreal/Content/Characters/BP_Judy.uasset b/Pawn_Unreal/Content/Characters/BP_Judy.uasset index 1575cff..ee4a245 100644 --- a/Pawn_Unreal/Content/Characters/BP_Judy.uasset +++ b/Pawn_Unreal/Content/Characters/BP_Judy.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bad47e6ba5d59a3b3b87c4f6f99513931d698e5bda42721987df04f4674c7faa -size 225770 +oid sha256:9832d56e18c422c2adc83644df67cfbfd9aadf2321286c45356ee8e6bf2f4bb3 +size 226968 diff --git a/Pawn_Unreal/Content/Input/IMC_Judy.uasset b/Pawn_Unreal/Content/Input/IMC_Judy.uasset index e86f722..2598668 100644 --- a/Pawn_Unreal/Content/Input/IMC_Judy.uasset +++ b/Pawn_Unreal/Content/Input/IMC_Judy.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ee32e50cfcc8a3a6ec74a0ac5256f7638975486b8f9a176a47283a601b15fc5 -size 7470 +oid sha256:65851d53eb61f87e3840a520d19fa3d924289ad22d980489f200fee8609fd02b +size 8099 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/91/BERD0JFDCI5RAYAPS4DY1P.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/91/BERD0JFDCI5RAYAPS4DY1P.uasset index 26931dd..34bce67 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/91/BERD0JFDCI5RAYAPS4DY1P.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/91/BERD0JFDCI5RAYAPS4DY1P.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:095b7423feef5e520436ca4ada169891edcdf403846c47993c4b3c326564c4c0 -size 4434 +oid sha256:ea41203b31cef6377aa3c85f0d7c1811b4361f623ddc21ba949c9481ddf091ef +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/XC/EES9G2P00B5EO5FPWE8N4U.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/XC/EES9G2P00B5EO5FPWE8N4U.uasset index 7889f07..52cb337 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/XC/EES9G2P00B5EO5FPWE8N4U.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/0/XC/EES9G2P00B5EO5FPWE8N4U.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b9aa98fa5ada2378494a0a3adb546a25770de0cc6cd8228a0001cd73ba5255e -size 4320 +oid sha256:48a4afd6ed50ec93b48527f71913a9f1030ef343171b7e952bd5d2dc2d32d9e0 +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/LE/RIS6ZA9R1KK7MS3EGVDKZV.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/LE/RIS6ZA9R1KK7MS3EGVDKZV.uasset index 6086bba..7c84048 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/LE/RIS6ZA9R1KK7MS3EGVDKZV.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/LE/RIS6ZA9R1KK7MS3EGVDKZV.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:745affce1b7cb5725c8e39f2fc6dbaf9b2cd0bf0b1ccd607aee11be4bf396b4a -size 4320 +oid sha256:8d97f7312712cd44144da64d0eee3f18fd0a662d9c9174c460dbd3db58bfdc9c +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/ZO/2T6IY0BJD55BZXPJC2K1VW.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/ZO/2T6IY0BJD55BZXPJC2K1VW.uasset index 5df3f60..874900c 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/ZO/2T6IY0BJD55BZXPJC2K1VW.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/1/ZO/2T6IY0BJD55BZXPJC2K1VW.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38f2cac41cbd652b2f5b053b478c5693e2d06abf72f5e5ce6a892966fb1e0910 -size 6644 +oid sha256:0c7f598bec02f86b8db92e2d961949d1537e6a25a2188fe59d7dc9f4f38c4838 +size 6803 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/7A/SCU1D1LU7J4NS7X4MENA7G.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/7A/SCU1D1LU7J4NS7X4MENA7G.uasset new file mode 100644 index 0000000..9fa1f49 --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/7A/SCU1D1LU7J4NS7X4MENA7G.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fb2c2cd2f7f0a5b2e9de10f83e32d1aed54adb0436615e8e87466a00ddeb089 +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/S1/Q0FDQMTTCM0M6RLWXOTPMF.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/S1/Q0FDQMTTCM0M6RLWXOTPMF.uasset index fa36887..7c36ac7 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/S1/Q0FDQMTTCM0M6RLWXOTPMF.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/S1/Q0FDQMTTCM0M6RLWXOTPMF.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:501e2333ad267b57cc6122c6d3a3e66e49189b324354a6a2807f4ba3115287a3 +oid sha256:2d5af35b2622837ddaca55c8fbf40b79b287882fbc27cde89907daf4e4847932 size 4441 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZC/SPZ5ZSIG7BW3194FWIBEWC.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZC/SPZ5ZSIG7BW3194FWIBEWC.uasset index 1f8e3db..0279701 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZC/SPZ5ZSIG7BW3194FWIBEWC.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZC/SPZ5ZSIG7BW3194FWIBEWC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f9ea1c60c5c4f236e2a2a0c625ee11dead9e78959268b6ff5ed5d2cb68b1034 -size 4318 +oid sha256:d446dc00a602ee2314f3139450c5139beb01ec210f410490804dcf17cabfc4cd +size 4352 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZX/V7QFRP3DMWRT8NZCJQYVRB.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZX/V7QFRP3DMWRT8NZCJQYVRB.uasset new file mode 100644 index 0000000..16d71aa --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/3/ZX/V7QFRP3DMWRT8NZCJQYVRB.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e77e28a7cb0d839eb28ac303f81b6a30d46f1c306155a6049708caba071ddf84 +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/4/OH/3YZOO5HQPTMDBNJPPM4RV0.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/4/OH/3YZOO5HQPTMDBNJPPM4RV0.uasset index c73cd19..ef0c747 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/4/OH/3YZOO5HQPTMDBNJPPM4RV0.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/4/OH/3YZOO5HQPTMDBNJPPM4RV0.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43e5a125722214af59e8d6753741d618e29e60b4cfe780767c3622a688d98755 -size 4320 +oid sha256:9461f90ef9909f371c63791fe25c706a04dc17d6263ffc0c8685eee65236a01b +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/5/V3/7TDZ89SOYDGI26YWXQ04D4.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/5/V3/7TDZ89SOYDGI26YWXQ04D4.uasset new file mode 100644 index 0000000..9dded55 --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/5/V3/7TDZ89SOYDGI26YWXQ04D4.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a261b9f01b1c2455a3917548963d356b3176859cf6b8523ff11388121efb7dab +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/IH/5T09JNLLSNG3U1KPWNFK3T.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/IH/5T09JNLLSNG3U1KPWNFK3T.uasset new file mode 100644 index 0000000..1e1c210 --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/IH/5T09JNLLSNG3U1KPWNFK3T.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d55fb4948de4b56060cab139887b7c3d48907d652ad659dec656cd49f483116 +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/RQ/JYNZEWOG6SXNE3HGUCSZIE.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/RQ/JYNZEWOG6SXNE3HGUCSZIE.uasset index 156b9f7..d7fcec2 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/RQ/JYNZEWOG6SXNE3HGUCSZIE.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/9/RQ/JYNZEWOG6SXNE3HGUCSZIE.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d122dde87e7da821fd2442dac088f30bce0029e8966d9ee672bff2bbab0d9ac -size 21547 +oid sha256:a21106cbd0ee258deaa6f407c978e30c15b7da120d8c4f4101592425534f7871 +size 110263 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/BZ/XZUBN8NU0UZ2W8T2MBWCYN.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/BZ/XZUBN8NU0UZ2W8T2MBWCYN.uasset index 1a4c3ef..85edb43 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/BZ/XZUBN8NU0UZ2W8T2MBWCYN.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/BZ/XZUBN8NU0UZ2W8T2MBWCYN.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9e0625781eae5876357ad47e5ea94ecd9d495730c6a35b2c59c6e00ddc6207f -size 4320 +oid sha256:dd4efef212868af4897f9c2fa1266ec7fd73ce93486f6ef66c93b1a945a0a96b +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/U2/4ECO0ADAHJEURYSR8OYVEX.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/U2/4ECO0ADAHJEURYSR8OYVEX.uasset index 9492931..8349326 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/U2/4ECO0ADAHJEURYSR8OYVEX.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/A/U2/4ECO0ADAHJEURYSR8OYVEX.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:410a4983d36e9dfef96f23e75b4fd6cf2949d669e83f0aefc4afd75970809285 -size 4320 +oid sha256:445e880fb237edeeb4bd4f3851b1963e9dacc065fd6c66e030b8822f97b91f47 +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/B/0Z/X5N2DTGOJMHQWHOBBKFNDX.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/B/0Z/X5N2DTGOJMHQWHOBBKFNDX.uasset new file mode 100644 index 0000000..957dd1c --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/B/0Z/X5N2DTGOJMHQWHOBBKFNDX.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f13050ce50d67fa4833b05b66e7e91740d2dfeced5edf69dae8bfa3042a3e317 +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/6U/C2O2TW7RAVDUQGZS7QLKMX.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/6U/C2O2TW7RAVDUQGZS7QLKMX.uasset new file mode 100644 index 0000000..963de28 --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/6U/C2O2TW7RAVDUQGZS7QLKMX.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35cd85e45d0ac186e19f003dc3e200b6120ebd78cd4517e3effcc17c6ce37ee7 +size 4468 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/BU/19EKBOJAPNXX8XDUPL0Z18.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/BU/19EKBOJAPNXX8XDUPL0Z18.uasset index 7898209..b9bb90a 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/BU/19EKBOJAPNXX8XDUPL0Z18.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/C/BU/19EKBOJAPNXX8XDUPL0Z18.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3503b03398da6182b14796e8abfaa5fc3ac374d9421c8d5c763a4331fa7e1acc +oid sha256:ef8302d77a4690b4c2325213b0810ef796f18b5f643b4eb7769f112c1e316821 size 10539 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/D/VD/LJAQY01SJR93NPW0FPCPIL.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/D/VD/LJAQY01SJR93NPW0FPCPIL.uasset index b15ea29..2c3d72a 100644 --- a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/D/VD/LJAQY01SJR93NPW0FPCPIL.uasset +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/D/VD/LJAQY01SJR93NPW0FPCPIL.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b4133eb7b55f9905d9e9547ab19e3acac3636c4821d3d48b7436b0cd5258f40 -size 4320 +oid sha256:28e095597c35d3ffbdb133e35e6deeafd6c9b343f8b65cd6c45ae0f2175f81c5 +size 4354 diff --git a/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/E/86/NKWQISSIQGRX07O716DOXO.uasset b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/E/86/NKWQISSIQGRX07O716DOXO.uasset new file mode 100644 index 0000000..244bbaf --- /dev/null +++ b/Pawn_Unreal/Content/__ExternalActors__/Maps/Dev/MAP_ControllerGym/E/86/NKWQISSIQGRX07O716DOXO.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a1ef219f83cb248783e23ab9ba98f87d4350994f21896701e5dbc753836b6bb +size 4468 diff --git a/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp b/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp index 1122cbe..318dc25 100644 --- a/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp +++ b/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp @@ -40,7 +40,48 @@ bool UPwnCharacterMovementComponent::IsMovingOnGround() const { return Super::IsMovingOnGround() || IsCustomMovementMode(SplineWalking); } +bool UPwnCharacterMovementComponent::IsFalling() const { + return Super::IsFalling() || IsCustomMovementMode(SplineFalling); +} + +void UPwnCharacterMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) { + Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode); + + // React to changes in the movement mode. + if (MovementMode == MOVE_Custom && CustomMovementMode == SplineWalking) + { + // make sure we update our new floor/base on initial entry of the walking physics + FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, false); + AdjustFloorHeight(); + SetBaseFromFloor(CurrentFloor); + } +} + +void UPwnCharacterMovementComponent::UpdateCharacterStateBeforeMovement(float DeltaSeconds) { + if (MovementMode == MOVE_Falling && IsFollowingSpline) { + SetMovementMode(MOVE_Custom, SplineFalling); + } else if (MovementMode == MOVE_Walking && IsFollowingSpline) { + SetMovementMode(MOVE_Custom, SplineWalking); + } + + Super::UpdateCharacterStateBeforeMovement(DeltaSeconds); +} + +void UPwnCharacterMovementComponent::PhysCustom(const float DeltaTime, const int32 Iterations) { + Super::PhysCustom(DeltaTime, Iterations); + + switch (CustomMovementMode) { + case SplineWalking: PhysSplineWalking(DeltaTime, Iterations); + break; + case SplineFalling: PhysSplineFalling(DeltaTime, Iterations); + break; + default: + UE_LOG(LogTemp, Error, TEXT("Invalid custom movement mode for PhysCustom call.")); + } +} + void UPwnCharacterMovementComponent::EnterSplineFollowMode() { + IsFollowingSpline = true; SetMovementMode(MOVE_Custom, SplineWalking); const UPwnGameplayModeSubsystem* Subsystem = GetWorld()->GetSubsystem(); @@ -53,10 +94,14 @@ void UPwnCharacterMovementComponent::EnterSplineFollowMode() { DistanceAlongSpline = OutDistanceAloneSpline; CombatPath = OutCombatPath; DotDirection = CombatPath->Reversed ? -1.0f : 1.0f; + FVector TargetLocation = CombatPath->Spline->GetLocationAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World); + TargetLocation.Z = UpdatedComponent->GetComponentLocation().Z; + UpdatedComponent->SetWorldLocation(TargetLocation); } } void UPwnCharacterMovementComponent::ExitSplineFollowMode() { + IsFollowingSpline = false; SetMovementMode(MOVE_Walking); } @@ -64,20 +109,44 @@ bool UPwnCharacterMovementComponent::IsCustomMovementMode(const ECustomMovementM return MovementMode == MOVE_Custom && CustomMovementMode == Mode; } -void UPwnCharacterMovementComponent::PhysCustom(const float DeltaTime, const int32 Iterations) { - Super::PhysCustom(DeltaTime, Iterations); - - switch (CustomMovementMode) { - case SplineWalking: PhysSplineFollow(DeltaTime, Iterations); - break; - default: - UE_LOG(LogTemp, Fatal, TEXT("Invalid custom movement mode")); - } +void UPwnCharacterMovementComponent::RecomputeTangentAndAcceleration() { + Tangent2D = CombatPath->Spline->GetTangentAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World).GetSafeNormal2D(); + // Recalculate acceleration so the input is relative to the spline + Acceleration = Tangent2D * Acceleration.Size2D() * FMath::Sign(Acceleration.X); } -UE_DISABLE_OPTIMIZATION +void UPwnCharacterMovementComponent::UpdatePawnVelocity(const float TimeTick) { + const FVector OldVelocity = Velocity; + const FVector OldLocation = UpdatedComponent->GetComponentLocation(); + const FVector VelocityNormal2D = Velocity.GetSafeNormal2D(); + const float VelocitySize2D = Velocity.Size2D(); -void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int32 Iterations) { + float VelocityDirection = Tangent2D.Dot(VelocityNormal2D); + if (VelocityNormal2D.IsNearlyZero()) { + VelocityDirection = 0; + } + VelocityDirection = FMath::Sign(VelocityDirection); // -1, 0 or 1 + + float NewDistanceAlongSpline = DistanceAlongSpline + VelocityDirection * VelocitySize2D * TimeTick; + NewDistanceAlongSpline = FMath::Clamp(NewDistanceAlongSpline, 0.0f, CombatPath->Spline->GetSplineLength()); + + FVector TargetLocation = CombatPath->Spline->GetLocationAtDistanceAlongSpline(NewDistanceAlongSpline, ESplineCoordinateSpace::World); + TargetLocation.Z = OldLocation.Z; + + const FVector Direction = (TargetLocation - OldLocation).GetSafeNormal2D(); + Velocity = VelocitySize2D * Direction; + Velocity.Z = OldVelocity.Z; + + DistanceAlongSpline = NewDistanceAlongSpline; +} + +void UPwnCharacterMovementComponent::UpdateDistanceAlongSpline() { + // Maintain coherent distance along spline with location + const float InputKey = CombatPath->Spline->FindInputKeyClosestToWorldLocation(UpdatedComponent->GetComponentLocation()); + DistanceAlongSpline = CombatPath->Spline->GetDistanceAlongSplineAtSplineInputKey(InputKey); +} + +void UPwnCharacterMovementComponent::PhysSplineWalking(const float DeltaTime, int32 Iterations) { if (DeltaTime < MIN_TICK_TIME) { return; } @@ -98,6 +167,7 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int bool bCheckedFall = false; bool bTriedLedgeMove = false; float RemainingTime = DeltaTime; + // Perform the move while ((RemainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations) && CharacterOwner && (CharacterOwner->Controller || bRunPhysicsWithNoController || HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocity() || (CharacterOwner->GetLocalRole() == @@ -109,51 +179,27 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int // Save current values UPrimitiveComponent* const OldBase = GetMovementBase(); - const FVector PreviousBaseLocation = OldBase != nullptr ? OldBase->GetComponentLocation() : FVector::ZeroVector; + const FVector PreviousBaseLocation = (OldBase != nullptr) ? OldBase->GetComponentLocation() : FVector::ZeroVector; const FVector OldLocation = UpdatedComponent->GetComponentLocation(); const FFindFloorResult OldFloor = CurrentFloor; - const FVector OldVelocity = Velocity; RestorePreAdditiveRootMotionVelocity(); + + // Ensure velocity is horizontal. MaintainHorizontalGroundVelocity(); + const FVector OldVelocity = Velocity; + + RecomputeTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */ Acceleration.Z = 0.f; + // Apply acceleration if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity()) { CalcVelocity(TimeTick, GroundFriction, false, GetMaxBrakingDeceleration()); } - float VelocityDirection = FMath::Sign(Velocity.X) * DotDirection; - float NewDistanceAlongSpline = DistanceAlongSpline + VelocityDirection * Velocity.Size() * TimeTick; - NewDistanceAlongSpline = FMath::Clamp(NewDistanceAlongSpline, 0.0f, CombatPath->Spline->GetSplineLength()); + ApplyRootMotionToVelocity(TimeTick); - FVector TargetLocation = CombatPath->Spline->GetLocationAtDistanceAlongSpline(NewDistanceAlongSpline, ESplineCoordinateSpace::World); - TargetLocation.Z = OldLocation.Z; - - FVector Direction = (TargetLocation - OldLocation).GetSafeNormal(); - - FHitResult MyHit; - - float DistanceOffset = NewDistanceAlongSpline - DistanceAlongSpline; - FVector Delta2 = TargetLocation - OldLocation; - SafeMoveUpdatedComponent(Delta2, UpdatedComponent->GetComponentQuat(), true, MyHit); - if (MyHit.Time < 1.0f) { - HandleImpact(MyHit, TimeTick, Delta2); - //DistanceAlongSpline += InputDotDirection * Velocity.Size() * TimeTick * MyHit.Distance; - } else { - DistanceAlongSpline = NewDistanceAlongSpline; - } - - if (OldLocation == UpdatedComponent->GetComponentLocation()) { - Velocity = FVector::Zero(); - } - - //continue; - - //Velocity = OldVelocity; - // Apply acceleration - if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity()) { - //CalcVelocity(TimeTick, GroundFriction, false, GetMaxBrakingDeceleration()); - } + UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ if (IsFalling()) { // Root motion could have put us into Falling. @@ -171,7 +217,7 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int if (bZeroDelta) { RemainingTime = 0.f; } else { - /*// try to move forward + // try to move forward MoveAlongFloor(MoveVelocity, TimeTick, &StepDownResult); if (IsFalling()) { @@ -187,10 +233,9 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int { StartSwimming(OldLocation, OldVelocity, TimeTick, RemainingTime, Iterations); return; - }*/ + } } - continue; // Update floor. // StepUp might have already done it for us. if (StepDownResult.bComputedFloor) { @@ -214,6 +259,9 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int // Try new movement direction Velocity = NewDelta / TimeTick; + + UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + RemainingTime += TimeTick; continue; } else { @@ -275,7 +323,7 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int } // Allow overlap events and such to change physics state and velocity - /*if (IsMovingOnGround()) { + if (IsMovingOnGround()) { // Make velocity reflect actual move if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && TimeTick >= MIN_TICK_TIME) { // TODO-RootMotionSource: Allow this to happen during partial override Velocity, but only set allowed axes? @@ -288,12 +336,328 @@ void UPwnCharacterMovementComponent::PhysSplineFollow(const float DeltaTime, int if (UpdatedComponent->GetComponentLocation() == OldLocation) { RemainingTime = 0.f; break; - }*/ + } } if (IsMovingOnGround()) { MaintainHorizontalGroundVelocity(); } + + UpdateDistanceAlongSpline(); /* -- PAWN MODIFICATIONS -- */ +} + +UE_DISABLE_OPTIMIZATION + +void UPwnCharacterMovementComponent::PhysSplineFalling(const float DeltaTime, int32 Iterations) { + if (DeltaTime < MIN_TICK_TIME) { + return; + } + + FVector FallAcceleration = GetFallingLateralAcceleration(DeltaTime); + FallAcceleration.Z = 0.f; + const bool bHasLimitedAirControl = ShouldLimitAirControl(DeltaTime, FallAcceleration); + + float RemainingTime = DeltaTime; + while ((RemainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations)) { + Iterations++; + float TimeTick = GetSimulationTimeStep(RemainingTime, Iterations); + RemainingTime -= TimeTick; + + const FVector OldLocation = UpdatedComponent->GetComponentLocation(); + const FQuat PawnRotation = UpdatedComponent->GetComponentQuat(); + bJustTeleported = false; + + RecomputeTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */ + + const FVector OldVelocityWithRootMotion = Velocity; + + RestorePreAdditiveRootMotionVelocity(); + + const FVector OldVelocity = Velocity; + + // Apply input + const float MaxDecel = GetMaxBrakingDeceleration(); + if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity()) { + // Compute Velocity + { + // Acceleration = FallAcceleration for CalcVelocity(), but we restore it after using it. + TGuardValue RestoreAcceleration(Acceleration, FallAcceleration); + Velocity.Z = 0.f; + CalcVelocity(TimeTick, FallingLateralFriction, false, MaxDecel); + Velocity.Z = OldVelocity.Z; + } + } + + // Compute current gravity + const FVector Gravity(0.f, 0.f, GetGravityZ()); + float GravityTime = TimeTick; + + // If jump is providing force, gravity may be affected. + bool bEndingJumpForce = false; + if (CharacterOwner->JumpForceTimeRemaining > 0.0f) { + // Consume some of the force time. Only the remaining time (if any) is affected by gravity when bApplyGravityWhileJumping=false. + const float JumpForceTime = FMath::Min(CharacterOwner->JumpForceTimeRemaining, TimeTick); + GravityTime = bApplyGravityWhileJumping ? TimeTick : FMath::Max(0.0f, TimeTick - JumpForceTime); + + // Update Character state + CharacterOwner->JumpForceTimeRemaining -= JumpForceTime; + if (CharacterOwner->JumpForceTimeRemaining <= 0.0f) { + CharacterOwner->ResetJumpState(); + bEndingJumpForce = true; + } + } + + // Apply gravity + Velocity = NewFallVelocity(Velocity, Gravity, GravityTime); + + //UE_LOG(LogCharacterMovement, Log, TEXT("dt=(%.6f) OldLocation=(%s) OldVelocity=(%s) OldVelocityWithRootMotion=(%s) NewVelocity=(%s)"), timeTick, *(UpdatedComponent->GetComponentLocation()).ToString(), *OldVelocity.ToString(), *OldVelocityWithRootMotion.ToString(), *Velocity.ToString()); + ApplyRootMotionToVelocity(TimeTick); + DecayFormerBaseVelocity(TimeTick); + + int32 ForceJumpPeakSubstep = 1; /* -- PAWN MODIFICATIONS -- */ + + // See if we need to sub-step to exactly reach the apex. This is important for avoiding "cutting off the top" of the trajectory as framerate varies. + if (ForceJumpPeakSubstep && OldVelocityWithRootMotion.Z > 0.f && Velocity.Z <= 0.f && NumJumpApexAttempts < + MaxJumpApexAttemptsPerSimulation) { + const FVector DerivedAccel = (Velocity - OldVelocityWithRootMotion) / TimeTick; + if (!FMath::IsNearlyZero(DerivedAccel.Z)) { + const float TimeToApex = -OldVelocityWithRootMotion.Z / DerivedAccel.Z; + + // The time-to-apex calculation should be precise, and we want to avoid adding a substep when we are basically already at the apex from the previous iteration's work. + const float ApexTimeMinimum = 0.0001f; + if (TimeToApex >= ApexTimeMinimum && TimeToApex < TimeTick) { + const FVector ApexVelocity = OldVelocityWithRootMotion + (DerivedAccel * TimeToApex); + Velocity = ApexVelocity; + Velocity.Z = 0.f; // Should be nearly zero anyway, but this makes apex notifications consistent. + + // We only want to move the amount of time it takes to reach the apex, and refund the unused time for next iteration. + const float TimeToRefund = (TimeTick - TimeToApex); + + RemainingTime += TimeToRefund; + TimeTick = TimeToApex; + Iterations--; + NumJumpApexAttempts++; + + // Refund time to any active Root Motion Sources as well + for (TSharedPtr RootMotionSource : CurrentRootMotion.RootMotionSources) { + const float RewoundRMSTime = FMath::Max(0.0f, RootMotionSource->GetTime() - TimeToRefund); + RootMotionSource->SetTime(RewoundRMSTime); + } + } + } + } + + if (bNotifyApex && (Velocity.Z < 0.f)) { + // Just passed jump apex since now going down + bNotifyApex = false; + NotifyJumpApex(); + } + + // Compute change in position (using midpoint integration method). + FVector Adjusted = 0.5f * (OldVelocityWithRootMotion + Velocity) * TimeTick; + + // Special handling if ending the jump force where we didn't apply gravity during the jump. + if (bEndingJumpForce && !bApplyGravityWhileJumping) { + // We had a portion of the time at constant speed then a portion with acceleration due to gravity. + // Account for that here with a more correct change in position. + const float NonGravityTime = FMath::Max(0.f, TimeTick - GravityTime); + Adjusted = (OldVelocityWithRootMotion * NonGravityTime) + (0.5f * (OldVelocityWithRootMotion + Velocity) * GravityTime); + } + + UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + + // Move + FHitResult Hit(1.f); + SafeMoveUpdatedComponent(Adjusted, PawnRotation, true, Hit); + + if (!HasValidData()) { + return; + } + + float LastMoveTimeSlice = TimeTick; + float SubTimeTickRemaining = TimeTick * (1.f - Hit.Time); + + if (IsSwimming()) //just entered water + { + RemainingTime += SubTimeTickRemaining; + StartSwimming(OldLocation, OldVelocity, TimeTick, RemainingTime, Iterations); + return; + } else if (Hit.bBlockingHit) { + if (IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit)) { + RemainingTime += SubTimeTickRemaining; + ProcessLanded(Hit, RemainingTime, Iterations); + return; + } else { + // Compute impact deflection based on final velocity, not integration step. + // This allows us to compute a new velocity from the deflected vector, and ensures the full gravity effect is included in the slide result. + Adjusted = Velocity * TimeTick; + + // See if we can convert a normally invalid landing spot (based on the hit result) to a usable one. + if (!Hit.bStartPenetrating && ShouldCheckForValidLandingSpot(TimeTick, Adjusted, Hit)) { + const FVector PawnLocation = UpdatedComponent->GetComponentLocation(); + FFindFloorResult FloorResult; + FindFloor(PawnLocation, FloorResult, false); + if (FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult)) { + RemainingTime += SubTimeTickRemaining; + ProcessLanded(FloorResult.HitResult, RemainingTime, Iterations); + return; + } + } + + HandleImpact(Hit, LastMoveTimeSlice, Adjusted); + + // If we've changed physics mode, abort. + if (!HasValidData() || !IsFalling()) { + return; + } + + // Limit air control based on what we hit. + // We moved to the impact point using air control, but may want to deflect from there based on a limited air control acceleration. + FVector VelocityNoAirControl = OldVelocity; + FVector AirControlAccel = Acceleration; + if (bHasLimitedAirControl) { + // Compute VelocityNoAirControl + { + // Find velocity *without* acceleration. + TGuardValue RestoreAcceleration(Acceleration, FVector::ZeroVector); + TGuardValue RestoreVelocity(Velocity, OldVelocity); + Velocity.Z = 0.f; + CalcVelocity(TimeTick, FallingLateralFriction, false, MaxDecel); + + UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + + VelocityNoAirControl = FVector(Velocity.X, Velocity.Y, OldVelocity.Z); + VelocityNoAirControl = NewFallVelocity(VelocityNoAirControl, Gravity, GravityTime); + } + + const bool bCheckLandingSpot = false; // we already checked above. + AirControlAccel = (Velocity - VelocityNoAirControl) / TimeTick; + const FVector AirControlDeltaV = LimitAirControl(LastMoveTimeSlice, AirControlAccel, Hit, bCheckLandingSpot) * LastMoveTimeSlice; + Adjusted = (VelocityNoAirControl + AirControlDeltaV) * LastMoveTimeSlice; + } + + const FVector OldHitNormal = Hit.Normal; + const FVector OldHitImpactNormal = Hit.ImpactNormal; + FVector Delta = ComputeSlideVector(Adjusted, 1.f - Hit.Time, OldHitNormal, Hit); + + // Compute velocity after deflection (only gravity component for RootMotion) + const UPrimitiveComponent* HitComponent = Hit.GetComponent(); + int32 UseTargetVelocityOnImpact = 1; /* -- PAWN MODIFICATIONS -- */ + if (UseTargetVelocityOnImpact && !Velocity.IsNearlyZero() && MovementBaseUtility::IsSimulatedBase(HitComponent)) { + const FVector ContactVelocity = MovementBaseUtility::GetMovementBaseVelocity(HitComponent, NAME_None) + + MovementBaseUtility::GetMovementBaseTangentialVelocity(HitComponent, NAME_None, Hit.ImpactPoint); + const FVector NewVelocity = Velocity - Hit.ImpactNormal * FVector::DotProduct(Velocity - ContactVelocity, Hit.ImpactNormal); + Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() + ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) + : NewVelocity; + } else if (SubTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported) { + const FVector NewVelocity = (Delta / SubTimeTickRemaining); + Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() + ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) + : NewVelocity; + } + + //UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + + if (SubTimeTickRemaining > UE_KINDA_SMALL_NUMBER && (Delta | Adjusted) > 0.f) { + // Move in deflected direction. + SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit); + + if (Hit.bBlockingHit) { + // hit second wall + LastMoveTimeSlice = SubTimeTickRemaining; + SubTimeTickRemaining = SubTimeTickRemaining * (1.f - Hit.Time); + + if (IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit)) { + RemainingTime += SubTimeTickRemaining; + ProcessLanded(Hit, RemainingTime, Iterations); + return; + } + + HandleImpact(Hit, LastMoveTimeSlice, Delta); + + // If we've changed physics mode, abort. + if (!HasValidData() || !IsFalling()) { + return; + } + + // Act as if there was no air control on the last move when computing new deflection. + const float VERTICAL_SLOPE_NORMAL_Z = 0.001f; /* -- PAWN MODIFICATIONS -- */ + if (bHasLimitedAirControl && Hit.Normal.Z > VERTICAL_SLOPE_NORMAL_Z) { + const FVector LastMoveNoAirControl = VelocityNoAirControl * LastMoveTimeSlice; + Delta = ComputeSlideVector(LastMoveNoAirControl, 1.f, OldHitNormal, Hit); + } + + FVector PreTwoWallDelta = Delta; + TwoWallAdjust(Delta, Hit, OldHitNormal); + + // Limit air control, but allow a slide along the second wall. + if (bHasLimitedAirControl) { + const bool bCheckLandingSpot = false; // we already checked above. + const FVector AirControlDeltaV = LimitAirControl(SubTimeTickRemaining, AirControlAccel, Hit, bCheckLandingSpot) * + SubTimeTickRemaining; + + // Only allow if not back in to first wall + if (FVector::DotProduct(AirControlDeltaV, OldHitNormal) > 0.f) { + Delta += (AirControlDeltaV * SubTimeTickRemaining); + } + } + + // Compute velocity after deflection (only gravity component for RootMotion) + if (SubTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported) { + const FVector NewVelocity = (Delta / SubTimeTickRemaining); + Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() + ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) + : NewVelocity; + //UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + } + + // bDitch=true means that pawn is straddling two slopes, neither of which it can stand on + bool bDitch = ((OldHitImpactNormal.Z > 0.f) && (Hit.ImpactNormal.Z > 0.f) && (FMath::Abs(Delta.Z) <= UE_KINDA_SMALL_NUMBER) && + ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f)); + SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit); + if (Hit.Time == 0.f) { + // if we are stuck then try to side step + FVector SideDelta = (OldHitNormal + Hit.ImpactNormal).GetSafeNormal2D(); + if (SideDelta.IsNearlyZero()) { + SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal(); + } + SafeMoveUpdatedComponent(SideDelta, PawnRotation, true, Hit); + } + + if (bDitch || IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit) || Hit.Time == 0.f) { + RemainingTime = 0.f; + ProcessLanded(Hit, RemainingTime, Iterations); + return; + } else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && OldHitImpactNormal.Z >= GetWalkableFloorZ()) { + // We might be in a virtual 'ditch' within our perch radius. This is rare. + const FVector PawnLocation = UpdatedComponent->GetComponentLocation(); + const float ZMovedDist = FMath::Abs(PawnLocation.Z - OldLocation.Z); + const float MovedDist2DSq = (PawnLocation - OldLocation).SizeSquared2D(); + if (ZMovedDist <= 0.2f * TimeTick && MovedDist2DSq <= 4.f * TimeTick) { + Velocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f); + Velocity.Y += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f); + Velocity.Z = FMath::Max(JumpZVelocity * 0.25f, 1.f); + + //UpdatePawnVelocity(TimeTick); /* -- PAWN MODIFICATIONS -- */ + + Delta = Velocity * TimeTick; + + SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit); + } + } + } + } + } + } + + if (Velocity.SizeSquared2D() <= UE_KINDA_SMALL_NUMBER * 10.f) { + Velocity.X = 0.f; + Velocity.Y = 0.f; + } + } + + UpdateDistanceAlongSpline(); /* -- PAWN MODIFICATIONS -- */ } UE_ENABLE_OPTIMIZATION diff --git a/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h b/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h index 6a2aa12..2df4ecf 100644 --- a/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h +++ b/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h @@ -29,6 +29,14 @@ protected: virtual bool IsMovingOnGround() const override; + virtual bool IsFalling() const override; + + virtual void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) override; + + virtual void UpdateCharacterStateBeforeMovement(float DeltaSeconds) override; + + virtual void PhysCustom(const float DeltaTime, int32 Iterations) override; + public: UFUNCTION(BlueprintCallable, Category="CharacterMovement") void EnterSplineFollowMode(); @@ -39,10 +47,16 @@ public: UFUNCTION(BlueprintCallable, Category="CharacterMovement") bool IsCustomMovementMode(const ECustomMovementMode Mode) const; - virtual void PhysCustom(const float DeltaTime, int32 Iterations) override; - private: - void PhysSplineFollow(const float DeltaTime, int32 Iterations); + void RecomputeTangentAndAcceleration(); + + void UpdatePawnVelocity(const float TimeTick); + + void UpdateDistanceAlongSpline(); + + void PhysSplineWalking(const float DeltaTime, int32 Iterations); + + void PhysSplineFalling(const float DeltaTime, int32 Iterations); public: UPROPERTY(BlueprintReadOnly) @@ -54,6 +68,9 @@ public: UPROPERTY(BlueprintReadOnly) float DotDirection; - UPROPERTY(BlueprintReadWrite) - float Input; + UPROPERTY(Transient) + bool IsFollowingSpline; + + UPROPERTY(Transient) + FVector Tangent2D; }; diff --git a/Pawn_Unreal/Source/Pawn/Public/Utils/EngineUtils.h b/Pawn_Unreal/Source/Pawn/Public/Utils/EngineUtils.h index 959acec..16e9b2d 100644 --- a/Pawn_Unreal/Source/Pawn/Public/Utils/EngineUtils.h +++ b/Pawn_Unreal/Source/Pawn/Public/Utils/EngineUtils.h @@ -7,3 +7,6 @@ #define PRINT_STRING_RED(Content, ...) PRINT_STRING_GENERIC(FColor::Red, Content, __VA_ARGS__); #define PRINT_STRING_YELLOW(Content, ...) PRINT_STRING_GENERIC(FColor::Yellow, Content, __VA_ARGS__); #define PRINT_STRING_GREEN(Content, ...) PRINT_STRING_GENERIC(FColor::Green, Content, __VA_ARGS__); + +#define BOOL_TO_TEXT(Bool) ((Bool) ? TEXT("True") : TEXT("False")) +#define BOOL_TO_STR(Bool) ((Bool) ? "True" : "False") \ No newline at end of file