From a07d88d99472ea4cd65efb2349b359a51bd49b4b Mon Sep 17 00:00:00 2001 From: Daniel Rupp <daniel.rupp@rwth-aachen.de> Date: Wed, 26 Jul 2023 13:57:53 +0200 Subject: [PATCH] Updated VRPawn to use EnhancedInputSystem, Renamed VRPawnMovement to ContinuousMovementComponent and added Snap turn and Handedness switching --- Content/BP_VirtualRealityPawn.uasset | Bin 0 -> 33285 bytes .../ContinuousMovementComponent.uasset | Bin 0 -> 15940 bytes .../Movement/IMC_MovementLeftHand.uasset | Bin 0 -> 16883 bytes .../Movement/IMC_MovementRightHand.uasset | Bin 0 -> 16148 bytes Content/Input/IMC_RWTH_Base.uasset | Bin 0 -> 13313 bytes .../InputActions/IA_DesktopRotation.uasset | Bin 0 -> 1409 bytes Content/Input/InputActions/IA_Fire.uasset | Bin 0 -> 1354 bytes Content/Input/InputActions/IA_Grab.uasset | Bin 0 -> 1354 bytes .../Input/InputActions/IA_LookUpRate.uasset | Bin 0 -> 1531 bytes Content/Input/InputActions/IA_Move.uasset | Bin 0 -> 1501 bytes .../Input/InputActions/IA_MoveForward.uasset | Bin 0 -> 1536 bytes .../Input/InputActions/IA_MoveRight.uasset | Bin 0 -> 1630 bytes Content/Input/InputActions/IA_MoveUp.uasset | Bin 0 -> 1511 bytes Content/Input/InputActions/IA_Turn.uasset | Bin 0 -> 1501 bytes Content/Input/InputActions/IA_TurnRate.uasset | Bin 0 -> 1521 bytes Content/Input/RWTHVRPawnInputConfig.uasset | Bin 0 -> 2660 bytes Content/RWTHVRGameMode.uasset | Bin 18898 -> 19364 bytes Content/TestMap.umap | Bin 0 -> 42864 bytes .../Pawn/ContinuousMovementComponent.cpp | 393 ++++++++++++++++++ .../Private/Pawn/VRPawnInputConfig.cpp | 5 + .../Private/Pawn/VRPawnMovement.cpp | 193 --------- .../Private/Pawn/VirtualRealityPawn.cpp | 227 +++++++--- ...vement.h => ContinuousMovementComponent.h} | 222 ++++++---- .../Public/Pawn/VRPawnInputConfig.h | 40 ++ .../Public/Pawn/VirtualRealityPawn.h | 111 ++++- Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs | 3 +- 26 files changed, 838 insertions(+), 356 deletions(-) create mode 100644 Content/BP_VirtualRealityPawn.uasset create mode 100644 Content/Components/Movement/ContinuousMovementComponent.uasset create mode 100644 Content/Components/Movement/IMC_MovementLeftHand.uasset create mode 100644 Content/Components/Movement/IMC_MovementRightHand.uasset create mode 100644 Content/Input/IMC_RWTH_Base.uasset create mode 100644 Content/Input/InputActions/IA_DesktopRotation.uasset create mode 100644 Content/Input/InputActions/IA_Fire.uasset create mode 100644 Content/Input/InputActions/IA_Grab.uasset create mode 100644 Content/Input/InputActions/IA_LookUpRate.uasset create mode 100644 Content/Input/InputActions/IA_Move.uasset create mode 100644 Content/Input/InputActions/IA_MoveForward.uasset create mode 100644 Content/Input/InputActions/IA_MoveRight.uasset create mode 100644 Content/Input/InputActions/IA_MoveUp.uasset create mode 100644 Content/Input/InputActions/IA_Turn.uasset create mode 100644 Content/Input/InputActions/IA_TurnRate.uasset create mode 100644 Content/Input/RWTHVRPawnInputConfig.uasset create mode 100644 Content/TestMap.umap create mode 100644 Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp create mode 100644 Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp delete mode 100644 Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp rename Source/RWTHVRToolkit/Public/Pawn/{VRPawnMovement.h => ContinuousMovementComponent.h} (55%) create mode 100644 Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h diff --git a/Content/BP_VirtualRealityPawn.uasset b/Content/BP_VirtualRealityPawn.uasset new file mode 100644 index 0000000000000000000000000000000000000000..de6a205060c6131ae6724b3246de1ef437f2b6ec GIT binary patch literal 33285 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 PvYgtl394y8e2{tois<|q literal 0 HcmV?d00001 diff --git a/Content/Components/Movement/ContinuousMovementComponent.uasset b/Content/Components/Movement/ContinuousMovementComponent.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9e004acae980e0517082dc4b43a66d1606b46956 GIT binary patch literal 15940 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 literal 0 HcmV?d00001 diff --git a/Content/Components/Movement/IMC_MovementLeftHand.uasset b/Content/Components/Movement/IMC_MovementLeftHand.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6762960518fa317c48d0effb0e173a6f09f2e763 GIT binary patch literal 16883 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 literal 0 HcmV?d00001 diff --git a/Content/Components/Movement/IMC_MovementRightHand.uasset b/Content/Components/Movement/IMC_MovementRightHand.uasset new file mode 100644 index 0000000000000000000000000000000000000000..c848335f083150cd0393bb64dac0495a9aaa949e GIT binary patch literal 16148 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 literal 0 HcmV?d00001 diff --git a/Content/Input/IMC_RWTH_Base.uasset b/Content/Input/IMC_RWTH_Base.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9f2e4864bdd16cd30a452191d9ac298cd579e460 GIT binary patch literal 13313 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 literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_DesktopRotation.uasset b/Content/Input/InputActions/IA_DesktopRotation.uasset new file mode 100644 index 0000000000000000000000000000000000000000..b1abe311863a7a6f076c4e2c2bd2d39dd306386d GIT binary patch literal 1409 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq?fyD+7Zm z0|SG8P<V((SWrlQeol5~iN0rEK`E4UOfJdH&nwpVbc}aNEzT~<F9^ynNd$?3v@>us zFff#XOlM$Vn99V!pvb_$U}|A*X=v_ZVB%u#=;-EbWNKjOY~pNTY~bkZ<Z58#3f2fx z%mh-*%)r0`)%+Fe+P};U3?R(Q!oaXf(@Cu4hqdFD&hJJN-#(P9-f+_R8nDoR&XOaF z8S@nmg6syF19Au}10w??!`W>)j6w_y4BFbEuBO^SsX3{M#i_ccdWH-jc?O1}>+v|` z!46|!U{GXY^#HMLSQr?5SwSoW{{R2~e+E_t29TYBP&p9gm!Fr)@P+v(uPg%tLqKA3 zc4B&}PkwS@PG%K2wEPltQ;Q1{lT#UlN-8_Z4)Z<2CHq7`5f+?Wlvz-s@0?$h8tR{v zm6}|_@WWHrP7F!TH7_GEFF7>@9GeVR*PVXD#=yYfoRe5w%urHZF;SR-fdNaj&Np|P z#{-fAB?3tFGYCl}@NhCPF!-jHB)TM)Br-f{*fN;|;ZufU_YP431Rt($uFjHFXsm)l z4IGYt|Ns9F(hKGw5$Njv{Qv)-9Z4R>gsB5L2we=u2hrHnfsz_X4jKOgMJbX1D3m~4 zP?7=#1c(iHJeY$@fRZ2xgG9l}1H?eW=we(@IZzUEgtB=+X$Z;&nFYd7E((Qi7K$Vq z2V@4w{oweAMkJ0nXJlbu_yP?ZkTx93lyIdkJ!CsT{sQ?iEHS4vHKej2l>rpnPWkyc zsfl?EAQ$;1mSmQtLQ<kFl9@=USr06$t`AM;C}|nlHg$06!H?#3Y+R7PKo}%PR51lA iOs9Wa+Ms8B<RZvVG9aBuxa^)qAj_%!ng&oYka_@w`X3?y literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_Fire.uasset b/Content/Input/InputActions/IA_Fire.uasset new file mode 100644 index 0000000000000000000000000000000000000000..aefe36aa0a33175490fbaf9b0b3a07ec2944a358 GIT binary patch literal 1354 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p1hLbD| z3|b5f4EjOgAs%5tA^G_^*_kE!o_PhOP|`8EBr`v+Sl`nz-Yv5z6>J(e0|P@g$VdhT zh7u+Q21N!21`7)}S4RVL6B83l6GJ0&S4&4TV{;2jM*~YY10w@x6R<{*VkQO#1`%ck z1{SF1D^MriVP;?e;Wx|-3>{ggmamSz>@|x)^29aG%RHAGmu%0FEplnDnpf}nRkI0X zH^>~2Ls%IY85kMPZp&d5Vqjp<)(&+w)ecI{Nlh$H)iu>KWB|!CFce*n#~}}P7$^wp z*1rR>1y~pu#8^Qr1pfd3|9?<Wf$Wrm%7G}q{Jd0#FU&`IWf>S40uqz66Vp?D@{<#D zGOH3n!RD8kn_66un4HQWR8rYNT0p=0l)YO7<gVc4qRfI4edqk5)KLGVtkmQZh991~ zc4A0!u6Y@WdC93M;CN%Wy6*HFHU<U;=bXgiVuq6PiisQy3=HrHENh?11LA|?7!tb- zLJ|o)oD2*MzNsaNE{P?H3{M)iOa^I!`iY^~y+c$0;YYZ-xjIWyq4CE63J_3`{r&&{ zKS(c#jf~ON{rUg@KRdDlC>y2@<RGXJ156CY2hlJwWOblK1(HL?|Nj5~kIV-J4Tuj) zJfMI8u|bgwqOoC6Vgq5294H}xXk?5o#s!rGB^F00`zn&dKxTn3R0@ScHw#4)jRP_R z<bH6ZLn9It?y!h{z|6p~7iup^EwY>vuEe8<Y#PW1Am4-~=9H#}R2HN%fXr~p&(BFs z%wquA?Uz`RS(XY(XtqdZA|+Nmu&la1G#R5LRb<=L!6gDenkTVwL7o9&kQ`A(52#d} g{%vW4p7oK7AV0}~bRyxhdlrE#r}k?aK*d1n0YdW)SO5S3 literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_Grab.uasset b/Content/Input/InputActions/IA_Grab.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d35442dff6c80662339e90fd71f0d084a42e7be6 GIT binary patch literal 1354 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p1hLbD| z3|b5f4EjOgAs%5tA^G_^*_kE!o_PhOP|`8EBr`v+Sl`nz-n}R>32YiS0|P@g$VdhT zh7u+Q21N!21`A_n3s+|&6B8FVQ%e&AQxjtsClf~}BS&W=3v(A2H?T&KVkQO#1`%ck z1{SF1D^MriVP;?e;Wx|-3{vNcp1oWD(|NkVMAox2n1t7h{q7NBe&X9-{PfY3xzBAt zc7x0TIfRvgk%5un?6w?6AqEBpZS7E3Q|+MCoYchPR9#a&Lk5sM14GgEcpUOzhcPfP z=zhH&4q^+iFffR*f>;Rr|NsC0pr8WTDFu}SQGWS(sSID3kMhbgFfarpCTAz6r~2e4 zC+1{UC4z#@FEKZ@xF9h(l|iVavV*jM=6YV4D*}p|;N+srf)ah_{G!xQ|D>$c<PwG- zp1O8oNOG=u8HstxsVU%iW4OBR^cyw?1_tMx#NuLxlJbg)91IK$un2sRJk_5E#0SMO zBz75uBocTy85kIRQ%e$E5=#;po-}Nk4AKPk6GO3kho}I;k8pK!b(W+;<BtIpAfO=o z`~UxckX{fQ8KbNF^Z);Uc4P%mHcTDJK~NzEm>7%?qG4jl>OhGKB!`Uu{r~?TnGXsY z5FeCyKmh?_gCZA1W5b}t2ErgYP(lFF$QWIW3n~dpERIn2RV0Uj%mQJk6bglI7K$Vq z2V@4w{oqK4MkFZQ;StTuz_1r;FGwx2oD#0Yqlau7$Oj<bgeB&briN4&q%wfaaLUim zNlnaS0NL%ASdv+m3Q1_TNM<4>Rz0w+x;``+qa;;i+tk4&0zaB3v2j730b!6FQAH1^ iRGj{8X@j2ik&7Td$$)es;j()cfh?!?YZ^esK<WX@xeg5g literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_LookUpRate.uasset b/Content/Input/InputActions/IA_LookUpRate.uasset new file mode 100644 index 0000000000000000000000000000000000000000..852c1b411c071ef591c0b9108d9b4d9c856c6c09 GIT binary patch literal 1531 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p1hCo&Z z1_K5L2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<?~|XO9a<2SSdt1hlb?Zs zp%7#+0|UcHCI$vY1_lO0BXcu17iUWoGgn7b7Xw2J3nN1p6DJENa}yI26H6noMv!7A z1_p+S%nS@HP|blL1_J{_Bnt#*vM?~Tzb&YH^v2e8#oNI6ud^2|-!W6`R<qN4*Y#)G z{J*c+^a5lz$Q+PESQ!`@7#YrP%V88^U|`VJ4s|uv4ob~QO)O5;HPtg@0Le2j6kU(U zArE#K0|Ue59cKzb?8z()498hPECl}l|NnnbfUz+!Fr0?Ui7_xRxPpTV5>R1@Ii;x~ zl?AB`f3IGCD2Ged%F3}Kv)IUmLFMJL9$t{Dywco&qWpr?qLNC6!0U4zK<4}9=cO`y zVLr+$%fP@8keHmEn4apBpPZPJS(ONi6~DyX)Z&7~<WvTslFAM)1_lO%^~{$$49JPu z-L1=IML;PcIJqdZphVv}zbG}-KPf9UxrE_|r>-5y!}?G;*Sw6xyyVmru>TpZt~>n( z6g18`iN(bXCFK<p`4|`&P}7a$!pb@xkSHjDK|+i{NFsrUlYxQ3H?<_uC9x!t;Yq`m z$s7nDGZedbhzcP1aCLKamZY*nqX%RFi23*b|NkHhKx}B*2QeAY)&2SZ|35cahJgW_ zI#8TIrQv!BsROwgqz75uzyJULgT!DM6iFa9$Zk^*1DXk;ObiN?#z7b)0ZM-$8X2RD zfr1<)56TygAO-^iLmMb9LD?X)Kp4tJq0r4jkwoKw%m9TmIO?Gh39=hCx?@=w7+j&Y zgLGi3P(n&-D9K3=*<_G!Kp}@n-k|t|W+9L>{1Qtt%TggJ&lbr@q|~bimQ}}^c9AVp z2bW2pG6m#isN)$xE&%aiaSUREG00mW26Ch$vq5qoHPG^nxKa{Sj8Ffzv_a4M$VHIN YG9XisaM?YJK$cVcH9;91Bn(my0N*A-Z~y=R literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_Move.uasset b/Content/Input/InputActions/IA_Move.uasset new file mode 100644 index 0000000000000000000000000000000000000000..2423ae65f120424dbd02c40d85d242ccd9832e18 GIT binary patch literal 1501 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p124hwR z1}z2#2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<@0(wi3O0?Ofq@|#WF!Lv z!+j<O21N!21`7*UH#Y+#V-pJ#b7M<aBS$Ag7fTB#a}zT+7Yk<>1F%MrVkQO#hI(cO z1{SDhdk}+xfx(>xg2P!D7+NEq-2Jd~t?!a$lC2NetPbDEk&gXmqwPL<jpbXzshv$A zyFuoF9Ky=L$iT>Oc3Tdk5Ca2)wsxqisdi9mPHJLts;;S?Ap=OBfuZPnJPvuV!x$JC zqLv={0Ag3OFfgoS1+ftL|NsC0K|#gFz`(E;DksLkz~BlFC`b^6CFYc-hEx`$GW@-I z`Jo&xSt~2Yip*jo7Y3D=%X)Z0s`5&61B&tsQj1C|83M1*bpV;~m!Fr)@P+v(uPg%t zLqKA3c4B&}PkwS@PG(giC_elWb5n~85|dLIgi0zqxEL515Y{ta?l2%NP8naFu@(WP zg5cz$%z_eq=lr77Q2(T?)Z`L|AD+5)Ag}5}<y`YJ67!N%Q@}oFxVrB28&IG)=Oh*v zGnAB9Oypo-V1T8S*Lez(JRm+OIYB~+K}aHjhm(PU!8f%e(Iv4Yk>N?hmdPMZP+u|> zyLX5RAbbs1H&<s#DmyevKn8%AfB*mg53&HnhNg57lL1}bpa1{=bAx3V7_g}W#RF6t zu9uKHkefkzkk$SB|NlQo42D4w17d^hHU%-DSq{p?pg`#wgh3LZGzX%QF}fHZR1%aI z9HHzCQ2K$gL1uw4l#4>4n}s5Y#sQfD3TJSnLn9L8e^^9&voJ8IL+u5rMV3=SN?0g~ zM-SOFkPkqig-FJrID}>ukkx*PC7ETZkaT8?WF%5r)dS0_V@;{ZmZ^iw9#Gi<@+Q>T z3?LVP_#n4~XgCIW2*g2-Xk<1>4x|QJjuBTHf(q*C-<CG$Ss%FwvRMXX3KA~6XA#J9 OYQH9=++<(?sRsc02r)hY literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_MoveForward.uasset b/Content/Input/InputActions/IA_MoveForward.uasset new file mode 100644 index 0000000000000000000000000000000000000000..1ada444b1705599dd9a36f797c71fba69c0524df GIT binary patch literal 1536 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p1hA>tJ z215o02K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<@0(wi>Xu(ro>-IuHk6-% zfuRUwG6Ms{7bXS<MFs{214lz6QwuXg69Y>}Q)f$412ZEh6BA2AH#cWTCl?E6uttz# zCI$wEDa;HEEKtoMAO-^iLktT9=dds^%+>a+G_7awp1#PSL*9Qu=^VZNJ<)|aj-BGp zBG+A1KZ5KAnFDeND+40~Bg5HkIgCOK3=G=Zp{}OdL8&>ZiN&e9rh0}9AbAFcqU-TE z<iQSOU|`tV<M$ZEp3cI+aE2AcLg4@Z|NjSt7#jlv!v(0E7y|=?D>%F$Ar+RGQ<@r5 zS&+)`_v+<`a=2uztQ;#ci;Y|uR9-IY;RUJ6E6oik$}dPQDyd`$ygt_fWWHa1UMj;E z=A*o_3=9kbiOJcC>8U>X$%#3cRf(Wz@k`82EiOn*PGt}(sqEkaMJU{Q=F1%hWJRsQ zCCQ&6pfnMjT$EW*qVJqvlp5-vl$DxX!tldW*AC=keW;vkUPfYGa%u`V02r>WJN*U} zHqJST#l;LI<rNb_F@u(JZvX1Z;bCB40BeDS8H12S0uLtx1A}jBNuo<)Ng~6OhAoph z5PoJTcJB}sK=9$}=ISg-Wrs!($N&)Y@Bjb*K^B16(BuzdGN7yb^Z);UZm<jk12%P_ zSb<8z^%7DCax+K|vbul&|NjSx!7wPAKx~lRrXU6=+CZ@lWusG|R1U%*aZm~b(a0EG zj1MXa${CJOb~lpwAhSRiDuqI!n}s5Y#sQfD3TNbq#1h{LEDQ`@Q0qZjuqaVNN^K}9 zN)Op=kbgj7hsa@|7=>mdkVE_uOESw+A&Jix$w;IGtOu4=$C`YREmH@VOCYC$JPma{ z1IPs+J}j0&Y%m7-3&cQ<c4Rh44x|QJ#t~Oig39vg-<CG$Ss%FwvRMXX3KA~6XA#J9 MYQLrdR1BmZ0CiS9+yDRo literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_MoveRight.uasset b/Content/Input/InputActions/IA_MoveRight.uasset new file mode 100644 index 0000000000000000000000000000000000000000..05aa4aff2ace49f2aaf67558affa2877d43a1f69 GIT binary patch literal 1630 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p1hHh2{ z27Lww2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<@0(wi8kCuyQ35tnh=GBj z0Awx$1A{Iz1A`(11B0Wfsk580xtocjqmi+po2jF#xv8<6lcR-&k)xTZiyK%YNHG%w z1H)Nn1_l<W=3Wqkfq`Kv3j{A^VPL5GE_(A{K(uH3e<N>SZVipu``29A(jDy5bYmAA z*Rf+EAiF{4fE>cgz{tSJaCTb`BZ#N19qMYT9h91rnpm8wYpQ3+0Fq~5D7qexLmuof z1_p-oKR>KN>~ky(3|ed;76SkO|NlQIyf_&c81$fWd>~I36(v>%6y+DB7L`;ocy4%{ zDaOFS;0g{yNa%$n=9H#}R2HN%{Jnblp&Tw*D=Wu}%wi)K29=k~dU!#q@=9~z1_xfB z>%hgpz~Gyol9`s7T2#!Scqb#9m4Si5FF!Ap;S2LoUVa7!2LGh2)Z`MlF2~!OU&%5s zFa#tfXD6no`s61k=44hSg5uOKF*miiATc?WL8zp%17tM9oy?az49G|q*O@LTiGY$* zaB@**L5aR|eo<;D#6=80Jaz3r0izF<bIr?0%u7y90f!UA)pe)efWp){C$YGgp`^TG zA}9b*5?z|%(<UAU1_rPONWd}(NhI)q;=?z!B+(_YB$44s!<NY$2>&t^yLX5RAoy@~ zb9I)ag1iihUXY<6`tSe$|3N_rVuQjJM1wH8x<CK_{|7}pNDiAikbNLI5Qga`q>dY; zn1O)-S>3<?|NldU!4xP&Kn#%DI1mGxfuT$c3Y0-W7$gBoydWAGql*bbB|(|W5z2mv z<RFk)APkj4q0r4jkwoKw%m9Tsazujsh7#K|Sr{1Vp_YR*U?@;RN@pl(Ne|gvkY7M? zfJoZxpezf?XCO!TC6;8Cr9zUOEs~K)NmmantBy6{B3q^oF3w;n2$Tvz9*35|3@|>( zGazvWkkdd6Xwe5^f-y`XC_8`!U^<|o0Tx4Mg9JcoKt2Ppi7JUfHO2IAOB?j8k6Z+~ XL<Xc2376fo2xK|6U(*0822u|I*UnMw literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_MoveUp.uasset b/Content/Input/InputActions/IA_MoveUp.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6f38343e58e4bddd4b43fd94258fa9c4929a4765 GIT binary patch literal 1511 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p123uAJ z1|0?l2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<@0(wi8d?B0kDq~oAs1vQ z0|UcTCI$vY1_lOWV<%^4b0<R+XG3Rm7Yid}V^=pb150B^M@JJg15;P9Mv!7A1_p*! zW(EcpsAg9XgMop;mj!}jSr{0CCQT7P8z$|vFwmr6N#637m-R~J*D5bKbVa{2?G9sI z3bGqy4#**_42%qn3}?6HFbXj+FlcLsx|(VSrRJn27N_c(>KQVC<QW)>uE*n$2Rn>` zf#LA&>?#nunT3I2Gb@ON!2kdM{|^c*HU<WU?NB)}1_lOKa8N-4DJ(IkG&Q8MAeG_o z)yog%aLHO(IaXvA8@Vv3yj<493sRL=nj27*Uyxc<Qppf_eXaw@e82p>RE96iM|ou# z7#IQ)ld}`kQ+@K26LT`F5<xNImzbMcT#%TY${<uy*}=uYz<{ux`ErK=sj+(JfYUk= zP)Z0+F3Kz@(Ra=-N)7c-%1TWxVff*xYX|bIK2**%FC#H8IW-0BcZRF$PQL*Ki*rt5 zaWO+ldBsGKYY^#0F;&-rhk=0stQrzh3_=nKJe;5?Pc2DwNi0cZc+#+CG6%w^48`sp zq5=rMFHGHBoh7O4&}ab}0Al|A|NlS80uURT-a$+Tbaj9J|NqYomSJGPrVbPrP-(bc zLh3+n2I)ao_wWDz{~$3K1_cL*4YJ!5#DHczC=-JMrEL%fNr2KFh(^ZfVti0ZP=0WP zvWq}z2+9VT1;S7+3WaVKiX<8bWCkdlks}i1H$+qivM?|hLTv`AMwL}UN?s^QNDtXW zkS{>`5Xl)7kI*awvfeMTB(p3PlG<#Mj6_PYdSF>~tmzfmGIekn1S&&7UWGcG0ptP@ z9~QSDHW-7v1Y#gZHZmI|2T}tq&xk7(K}GiTZ%Z5WtdCp-*(?Jx1qqkkvj}84wO`W! IDh5&y02YWdcK`qY literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_Turn.uasset b/Content/Input/InputActions/IA_Turn.uasset new file mode 100644 index 0000000000000000000000000000000000000000..480126b060800e5a122f0f0f2f6c6bdefc7a7469 GIT binary patch literal 1501 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p124hwR z1}z2#2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<A5vPB2R4nLfq@|#WF!Lv z!+j<O21N!21~+3z6GInQ3lkG_6EjOoM@u7fLl+YZ17{O+V<R^sL$F4WVkQO#hI(cO z1{SDhdk}+xfx(>xg2P!D7?j>=)*m$9>e8cqSbkB{O#Zi*Ygc_px#YB{YvqpzS>ci( zyFuoF9Ky=L$iT>Oc3Tdk5Ca2)wsxqisdi9mPHJLts;;S?Ap=OBfuZPnJPvuV!x$JC zp1m*w8OKn~!oaYS6($Oy|NsC09~4w<3=9lwp>kpj3=FQ|fPw^3SYl3TYDi^4D#PEa zmmkXElC`pOtjH`ja$!(;xvYm5q$;m8H=roLAhoEZk|FT=TnCW(e))N+3}2X!^2#zW zFa#tfXD6no`s61k=44hSg5tw3F*miiATc?WL8zp%gNuQI0bxD!<qiYV;#5BJRl5i% z6$B?2Wfqj^JLearhWaOEr6!j!{P5JZ19?>+D(9M)k(ig9ngaGY!_{@C-+%(eIVZ8W zn4zS+Vj>3v0|P9re7(!Iln2BIB_~KIF$hT{@NhCPF!-jHB)TM)Br-f{*fJTU3F=FR zV)qVF0fevN>gMV!No9vb2Ls3e5cBW<|NlW2fY{KK4q`H(tNZi+|9@_<3<Co;b)a~F zO2hRMQU`J~NDs2QfB*mg2Z_NjC}Kctklm&r1~kh-nHUr(U4t-40+i-JG%`jP<AX|q z@`59jodHTeP&UXc5QcJ5D0H(>B+)n^GeF@Cj&x{5g8UDQXm1t<26d>tAhpPHN=OL{ zCGqGXn+Ea$D6|mC7!-%ltOByyFR>)EEEST@Y>|vaN~?NcS#_)_71=U%aM=US><kPH zP-ioMTmbSm$n78+jzJy*agZY#nGKQysezVb#Fd7if_nP5r44%4M=pYFmI0Z9gv;(( P1hSmkuW0}k1E~i9Xgo1| literal 0 HcmV?d00001 diff --git a/Content/Input/InputActions/IA_TurnRate.uasset b/Content/Input/InputActions/IA_TurnRate.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d5e0153f09345b265bacba76e67eafc4ee5e5775 GIT binary patch literal 1521 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p126t8l z20aD_2K}J$5Rb5+ko^3d?938<&%A<CDCwA7l9``ZtncX<A5vPB7nE3%3O13Sfq@|( zWGn*%!)qo621N!21`|g!OIHI^R}({XS4Sg5BQqyQOG85^HxnZZM>8WcGq6UGVkQO# zhHhpC1{SDhZxDllfgzX$f|FSo7z$R;e;HaB?>!}}@&praOiJp+tQjqHK6!Uv`0rR) zb2SxYH^>~2Ls%IY85kMPZp&d5Vqjp<)(&+w)ecI{Nlh$H)iu>KWB|!CFce*n#~}}P z7y|>ttp$Y_K<sW728O+?AQl4u|Ns9#D7e@d7#I#h<-`~m7+k@D1qr6G#GKO9kjjEo zhQC)YKa|5IYh~qFky&iy!l3eUSr0ErRbFXsKv8}{YEelgL*Vtf4j}XW^7B#|zAzu< zm1SUH2uMuMPE1er$xlwq$*f8Q#fe{HZfbEsVsa{jP)TJ67Xt$W!g}V*9R{Swt4Ybc zQzD?$5S(0;Sx}<yoL`h0>YtRAnq0#0!&BD|<XwHJoNHc2VqS7;3fT7ySJ$0>0}2@D zoW$Z{hLZA%i98Gp49IC_x={Hmuox(5K|+f`NFsrUlYxQ3H?<_uC9x!t;Yq`m$s7pZ zG8DUahzcP1aCLKamZY*nqXuLEi23*b|NkHhKx}BL2QeAY)&2SZ|35cahJgW_I#7H- zrQv!BsROwgqz75uzyJULgT!DM6hR<1$Zk^*1DgGyObiN?zCjoy0ZMxy8X2RD@j)d) zdBPFOt^uVZC>vxJ2t&Cj6uMa`l4u-|8K7_mM?5ql!F;3wBAkVR!5V5eTs@M!5>g^V zNl1Fgrh<F|3N=Ks2E`>b>wv8EODxGOONFF4TO=cq(ybm?Rvl}qMYc>GTo!@K5|DSH zPG<nQ0K|vIFNh7sAWwl9$Ptdr2FZccK+84aN=H!PJ^kC#20iN|7eO}5fJ{NcW%n!s OSx)WOG=PeM)B^wm7&<5b literal 0 HcmV?d00001 diff --git a/Content/Input/RWTHVRPawnInputConfig.uasset b/Content/Input/RWTHVRPawnInputConfig.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ad08269ac07bbd98e93ddee7572a9262a0a32c63 GIT binary patch literal 2660 zcmX@utTpe)|Ns9Jm>C$jm>3v7Ff)JwBLf4&+l&oQUOG+r!hOqdk>hSRJq-p122)N3 z25klg2K}J$5Rb5+ko^3d?938<&%A=t5~x@}VtF2z@0_2PmYEJVPJw}eAqQk80|Ubq z76t}I1_lOa7c)m!Gb2|M6AM=pb7Kp0BQr}2Hwz<E7YkQsV`mqzMv!7A1_p)@b_NCk zsAdrkuoQy{2Ll+pa4;~`=DiA9%24mUc+0Q5{1ZccT%#Z8%q!OO>o_`{*_v<uVvyY+ zb3hJZWng4rWH`Gmhf#=ufk9h4)YViwC^aWFu{c%NRL_tBB+tN5bUhx2Jj8YehJLl3 zejs)Y2Lpov7l?(x|NsC04+<+z1_lNbs2s@sE~&-YCHVzG`6Y=ZnfZAPY63mAtPBhc zZka`?49{ixgh3qlqQoQyb`F`NAdYW-St<h$>&nUO3=9k)PG|wcr$1X>gXH}3^HLeU zFdyaRXJBCPPs&P7E(s{gFGwvasbp}xz4?_a0|P@qVsdt3da6%;a$-(q71&n4#N5>4 zg2d!h2BDJ54v;}1rA2uRLfiWFO>xC7I7S_lL5?og_jHU$_sAaMl6_jlY6koE)u-&; zMC$+t3D@(=T%vV=f{KCh)fsCYVqFE&5n8}-=YZ2XqHO^OoP6Y~b`mp1hUu$MA|Qta zCl_TFl;}I>7o~<mVvym7r>>nClALQ^Mq*xaY6>_C7_P26{RUY!2stA%aG(7;nGKY= zauSP+8A{44CJHk!Fkp$V`Q~o(K>5hiF&-Qn3}x*zp?q*yKS-YH59Ncxh2eFcf+Q#% zdpgF0_@M<1imAE|P!-@%_<EOZDG$gpP+0&;DGWjq2|S=I;G0^K=#p5H$nd0L%Vdxy zXsTo=cJB}c<qIeuu5PZ*l2ni{A-+Q|Qk2CM1VDK=EC^j@+JAp>ka?h-2=Y0I{`mj@ ze`yedfq?;(*r7~tc>-d?)P4B>|Gzv)08O1BR7?s=fB*mgKPc`%Tx9dm)qVT_|Gy|m z5drhQ{{R0U<X4bVeCB=m|Np-@ND%?^KL7v!UxEmApZ@><FNsAREc|4kav%y6i69!; zec052k_a*CxIv20{DI9pP=W#}C1fuzND&d{@e!#G<`zQsf>I&KNMv=OvL7S{!=O?a z#0J$rjvxlK9)&U)Ky?!c!}!QFx>+E(fB*mg2hlJr2W5c5bQhFe28%E#ADIH#1;QXP zZ03W+L1oC#|NsC0{r~^}pa1{=|Nj5~|F8f5|AX2JAbDgAG7A(An7I*@s<G!_3l0Vb zO{g0|`f;lSwL0{nr6j~GXrn_9(>{<%pcLbmSdv+m>I^OeosmjXq*7K7EUQkW$?D+3 z0v7DZkqrt6khP%N8N?>4Wy1ne$-uw>3S|%z#%BNpIY<D6F(g580kR6KBq)+V2@I<w zDE6QYJ4`!4(F!V(vFZfHH?ns@?ZxTemNw{FAGrv!4!Mi~8Bli5B9P_OeoX@qhk=0s Gq#ghb>?Yp; literal 0 HcmV?d00001 diff --git a/Content/RWTHVRGameMode.uasset b/Content/RWTHVRGameMode.uasset index ea7345658120735d8871d7bf4d19a7abddd7c4d5..c3904e554dfa66dd200f0e61e318efac52b09a3a 100644 GIT binary patch delta 2980 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 delta 2171 zcmZ27o$=CSM#+QCTJwJV|NlRMnSp_eiGkq_GXofKPSmv)wPs*oXuMjRx!TeH*_%MG zRT|s2@3Wp*>(8h%@vOM`ToVQcRt5$Jzx=#Z1_lNehFk^)hFy%44H)%(c^DWN%J>)< zgc%qZ>bMyg+JqSx7#SECehD)$Fo`fQ$cZp8{A<0vy3wnuS=xEZRN+OdZnt0Kc+u%7 zywFo++D2Rb+&>`oAaimjPqbAxe4os@1mv<B5e9~Ho*)(i|NsC0Kgcyf3=9k)HVErZ z{Hq|M7Ze`i5f<d0n49XGpOVVZ){>>T*_Y`A>tr2{+mmHEb0+>#V#(o|?K62BXONs2 z0|SG8aB@**L5V&@V@Q5}PIhJq1NYgllP4>3Ic#p^GG`R$XJBA}$ioe8xxZ3!a=xhO z<g?t;j+_h(48hL9@qQpH4KH6h3JQg=%%YOg#GIhi#GK5M%7Dc3JO)8F&g~&kUqnIa z5C8xFkDq+dL0A-<OcG9+WRQ#?x}Nuw|MQAt5#7%tj$!`0|NsAogG|9H69JOJFzMao zcwX!##evjgH7OCNOcqX=Y@9OgSY_V+|NlP+BqJCK4c9~{{ciGlBp0VaML|?BNIfSk z1k)xz=aWa`@$)0|%=zWje*XXeAEXb4GoTD^1_lNVXfB#-GI_tP*5p_GGHReq_6<o( zCR8~nvFw4ekD5=O2v)A31Qh{ingl3^gMoozBb41}F*(pqe{#Q&xY~ClQ$cygAF2eT zd@Yo1WC>UP4@o)5nMn+gtO-(X0}^0hV7QN8ITHf|14y|K3&eP7TbSvpf00ZF$tEM2 zJ^|!91_p-o$qyZjCtng&kOFB>2N4Vm3?M_*LFEYp1H*SGaJV@La7>;gq#_mrRbt4% zz@Q0b%Xz@;RQrl#C&-jos1lG;H7MI1q5KPya!~n_jpW2<Q02J@<)4w1gG_fvQeF*J z9_$Hoj3mg@AX7mh31Wk^fY>~f7mHXcf_NbO5o#zXM7W>^f)Y;9<csFYleI)unb<@o z2Z|cA35hT;^qNfW5|w2Eshhk@^dO4|0|Ue4KCuWEc@X2Dm??}QKRHW$8}I#(|Nk>H zFoI#=W?hL!F+l+^hmj$KA%!80A)g^_@_Jp-&7brhuugX5l9~M1#0<#@(_jSWi>U)U zsPy{(pMA2mnG)l-$#G^1jP8>+dP+=A@MdB3n=J3GGWmd6mD*g8R~Q%=yr4k>j!y$9 z2PT*?Il){*5XMu6%6d$mD4;M|g-2S_87cx(ApqqEPM&D1!RRviySWZrw>VS|tXl&V zc&=rsc_r>ei3J%9cnlJd<~uNxv*rL;`>x6ZAX8N)U$(Gk^qSmYr7_vsQU>lp0mg5W z8!f{`ky98*9}iax!|%y&EY%rZCvWtWnQUO?57zAh<v+CY2J<y0hgz#JdQGmfc4YiD zd5`sKM$gFy?F*oCjJD0JzyAOK?*L9b4gxHb8|-u^zqHk0^qKt5dG_SBb{t50WhRT* zuR+lt<<QLdYw|sZ9;kXYr{Box8T}_GI*UW?TIlQpR&@ka-Y|d?q}$~69v3)4i4o45 z-0ixB(RZ?g+YUzG%|G4r7#V#hYk1U6uJ`m}{51KLrzxWlDCSu~!66J{@q*GMs5%4% niudFoFH_C{5EGORCQtC{V&tAI?XAHIiW~`$dPeTaiQd8hYf1u0 diff --git a/Content/TestMap.umap b/Content/TestMap.umap new file mode 100644 index 0000000000000000000000000000000000000000..7e7113328a4bc24bfdcbdc7f61a83af3a8cc305f GIT binary patch literal 42864 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 literal 0 HcmV?d00001 diff --git a/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp new file mode 100644 index 00000000..c931c7c2 --- /dev/null +++ b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp @@ -0,0 +1,393 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Pawn/ContinuousMovementComponent.h" + +#include "DrawDebugHelpers.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "Camera/CameraComponent.h" +#include "Components/CapsuleComponent.h" +#include "Engine/LocalPlayer.h" +#include "GameFramework/PlayerController.h" +#include "Pawn/VRPawnInputConfig.h" +#include "Utility/VirtualRealityUtilities.h" + +UContinuousMovementComponent::UContinuousMovementComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) +{ + CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); + CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); + CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); + + VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); +} + +void UContinuousMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ + + SetCapsuleColliderToUserSize(); + + FVector PositionChange = GetPendingInputVector(); + + if (NavigationMode == EVRNavigationModes::NAV_WALK) + { + PositionChange.Z = 0.0f; + ConsumeInputVector(); + AddInputVector(PositionChange); + } + + if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) + { + MoveByGravityOrStepUp(DeltaTime); + CheckForPhysWalkingCollision(); + + if(CheckForVirtualMovCollision(PositionChange, DeltaTime)) + { + ConsumeInputVector(); + } + } + + if(NavigationMode == EVRNavigationModes::NAV_NONE) + { + ConsumeInputVector(); + } + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + LastHeadPosition = HeadComponent->GetComponentLocation(); +} + +void UContinuousMovementComponent::BeginPlay() +{ + Super::BeginPlay(); + + SetUpdatedComponent(VRPawn->GetRootComponent()); + SetHeadComponent(VRPawn->CapsuleRotationFix); + + SetupInputActions(); + +} + +bool UContinuousMovementComponent::CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime) +{ + FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; + FHitResult FHitResultVR; + CapsuleColliderComponent->AddWorldOffset(ProbePosition, true, &FHitResultVR); + if (FVector::Distance(FHitResultVR.Location, CapsuleColliderComponent->GetComponentLocation()) < CapsuleColliderComponent->GetScaledCapsuleRadius()) + { + return true; + } + return false; +} + +void UContinuousMovementComponent::SetHeadComponent(USceneComponent* NewHeadComponent) +{ + HeadComponent = NewHeadComponent; + CapsuleColliderComponent->SetupAttachment(HeadComponent); + const float HalfHeight = 80.0f; //this is just an initial value to look good in editor + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, HalfHeight); + CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,HalfHeight)); +} + +void UContinuousMovementComponent::SetCapsuleColliderToUserSize() +{ + float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - HeadComponent->GetComponentLocation().Z); + + if (CharachterSize > MaxStepHeight) + { + float ColliderHeight = CharachterSize - MaxStepHeight; + float ColliderHalfHeight = ColliderHeight / 2.0f; + if (ColliderHalfHeight <= CapsuleRadius) + {//Make the collider to a Sphere + CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); + } + else + {//Make the collider to a Capsule + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); + } + + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); + CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); + CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); + } + else + { + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); + CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); + } +} + +void UContinuousMovementComponent::CheckForPhysWalkingCollision() +{ + FVector CurrentHeadPosition = HeadComponent->GetComponentLocation(); + FVector Direction = CurrentHeadPosition - LastHeadPosition; + FHitResult FHitResultPhys; + CapsuleColliderComponent->AddWorldOffset(Direction, true, &FHitResultPhys); + + if (FHitResultPhys.bBlockingHit) + { + UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth); + } +} + +void UContinuousMovementComponent::MoveByGravityOrStepUp(float DeltaSeconds) +{ + FVector StartLineTraceUnderCollider = CapsuleColliderComponent->GetComponentLocation(); + StartLineTraceUnderCollider.Z -= CapsuleColliderComponent->GetScaledCapsuleHalfHeight(); + FHitResult HitDetailsMultiLineTrace = CreateMultiLineTrace(FVector(0, 0, -1), StartLineTraceUnderCollider, CapsuleColliderComponent->GetScaledCapsuleRadius() / 4.0f, false); + float DistanceDifference = abs(MaxStepHeight - HitDetailsMultiLineTrace.Distance); + //Going up (in Fly and Walk Mode) + if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight)) + { + ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1); + } + //Gravity (only in Walk Mode) + else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.GetActor() == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f))) + { + ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1); + } +} + +void UContinuousMovementComponent::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction) +{ + VerticalSpeed += VerticalAcceleration * DeltaSeconds; + if (VerticalSpeed*DeltaSeconds < DiffernceDistance) + { + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds)); + } + else + { + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * DiffernceDistance)); + VerticalSpeed = 0; + } +} + +void UContinuousMovementComponent::SetupInputActions() +{ + // simple way of changing the handedness + if(bMoveWithRightHand) + { + MovementHand = VRPawn->RightHand; + RotationHand = VRPawn->LeftHand; + IMCMovement = IMCMovementRight; + } else + { + MovementHand = VRPawn->LeftHand; + RotationHand = VRPawn->RightHand; + IMCMovement = IMCMovementLeft; + } + + const APlayerController* PlayerController = Cast<APlayerController>(VRPawn->GetController()); + UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()); + if(!InputSubsystem) + { + UE_LOG(LogTemp,Error,TEXT("InputSubsystem IS NOT VALID")); + return; + } + + InputSubsystem->ClearAllMappings(); + + // add Input Mapping context + InputSubsystem->AddMappingContext(IMCMovement,0); + + UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(VRPawn->InputComponent); + if(!EI) + { + UE_LOG(LogTemp,Error,TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement")); + return; + } + + // walking + EI->BindAction(InputActions->Move, ETriggerEvent::Triggered, this, &UContinuousMovementComponent::OnBeginMove); + + // turning + if(bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode()) + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Started, this, &UContinuousMovementComponent::OnBeginSnapTurn); + } else + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Triggered, this, &UContinuousMovementComponent::OnBeginTurn); + } + + // bind functions for desktop rotations only on holding down right mouse + if (UVirtualRealityUtilities::IsDesktopMode()) + { + APlayerController* PC = Cast<APlayerController>(VRPawn->GetController()); + if (PC) + { + PC->bShowMouseCursor = true; + PC->bEnableClickEvents = true; + PC->bEnableMouseOverEvents = true; + } + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Started, this, &UContinuousMovementComponent::StartDesktopRotation); + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Completed, this, &UContinuousMovementComponent::EndDesktopRotation); + } +} + + + +FHitResult UContinuousMovementComponent::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility) +{ + //Re-initialize hit info + FHitResult HitDetails = FHitResult(ForceInit); + + FVector End = ((Direction * 1000.f) + Start); + // additional trace parameters + FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, NULL); + TraceParams.bTraceComplex = true; //to use complex collision on whatever we interact with to provide better precision. + TraceParams.bReturnPhysicalMaterial = true; //to provide details about the physical material, if one exists on the thing we hit, to come back in our hit result. + + if (Visibility) + DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1); + + if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams)) + { + if (HitDetails.bBlockingHit) + { + } + } + return HitDetails; +} + +FHitResult UContinuousMovementComponent::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility) +{ + TArray<FVector> StartVectors; + TArray<FHitResult> OutHits; + FHitResult HitDetailsMultiLineTrace; + HitDetailsMultiLineTrace.Distance = -1.0f;//(Distance=-1) not existing, but to know if this Variable not Initialized(when all Traces not compatible) + + StartVectors.Add(Start); //LineTraceCenter + StartVectors.Add(Start + FVector(0, -Radius, 0)); //LineTraceLeft + StartVectors.Add(Start + FVector(0, +Radius, 0)); //LineTraceRight + StartVectors.Add(Start + FVector(+Radius, 0, 0)); //LineTraceFront + StartVectors.Add(Start + FVector(-Radius, 0, 0)); //LineTraceBehind + + bool IsBlockingHitAndSameActor = true; + bool IsAllNothingHiting = true; + // loop through TArray + for (FVector& Vector : StartVectors) + { + FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility); + OutHits.Add(OutHit); + IsBlockingHitAndSameActor &= (OutHit.GetActor() == OutHits[0].GetActor()); //If all Hiting the same Object, then you are (going up/down) or (walking) + IsAllNothingHiting &= (OutHit.GetActor() == nullptr); //If all Hiting nothing, then you are falling + } + + if (IsBlockingHitAndSameActor || IsAllNothingHiting) + HitDetailsMultiLineTrace = OutHits[0]; + + return HitDetailsMultiLineTrace; +} + +void UContinuousMovementComponent::StartDesktopRotation() +{ + bApplyDesktopRotation = true; +} + +void UContinuousMovementComponent::EndDesktopRotation() +{ + bApplyDesktopRotation = false; +} + +void UContinuousMovementComponent::OnBeginMove(const FInputActionValue& Value) +{ + const FVector ForwardDir = UVirtualRealityUtilities::IsDesktopMode() ? VRPawn->Head->GetForwardVector() : MovementHand->GetForwardVector(); + const FVector RightDir = UVirtualRealityUtilities::IsDesktopMode() ? VRPawn->Head->GetRightVector() : MovementHand->GetRightVector(); + + if (VRPawn->Controller != nullptr) + { + const FVector2D MoveValue = Value.Get<FVector2D>(); + const FRotator MovementRotation(0, VRPawn->Controller->GetControlRotation().Yaw, 0); + + // Forward/Backward direction + if (MoveValue.X != 0.f) + { + VRPawn->AddMovementInput(ForwardDir, MoveValue.X); + } + + // Right/Left direction + if (MoveValue.Y != 0.f) + { + VRPawn->AddMovementInput(RightDir, MoveValue.Y); + } + } +} + +void UContinuousMovementComponent::OnBeginTurn(const FInputActionValue& Value) +{ + if(UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation) return; + if (VRPawn->Controller != nullptr) + { + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + VRPawn->AddControllerYawInput(TurnRateFactor * TurnValue.X); + if (UVirtualRealityUtilities::IsDesktopMode()) + { + UpdateRightHandForDesktopInteraction(); + } + } + + if (TurnValue.Y != 0.f) + { + if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + { + VRPawn->AddControllerPitchInput(TurnRateFactor * TurnValue.Y); + SetCameraOffset(); + } + } + } +} + +void UContinuousMovementComponent::OnBeginSnapTurn(const FInputActionValue& Value) +{ + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + VRPawn->AddControllerYawInput(SnapTurnAngle); + } +} + +void UContinuousMovementComponent::SetCameraOffset() const +{ + // this also incorporates the BaseEyeHeight, if set as static offset, + // rotations are still around the center of the pawn (on the floor), so pitch rotations look weird + FVector Location; + FRotator Rotation; + VRPawn->GetActorEyesViewPoint(Location, Rotation); + VRPawn->CameraComponent->SetWorldLocationAndRotation(Location, Rotation); +} + +void UContinuousMovementComponent::UpdateRightHandForDesktopInteraction() +{ + APlayerController* PC = Cast<APlayerController>(GetController()); + if (PC) + { + FVector MouseLocation, MouseDirection; + PC->DeprojectMousePositionToWorld(MouseLocation, MouseDirection); + FRotator HandOrientation = MouseDirection.ToOrientationRotator(); + VRPawn->RightHand->SetWorldRotation(HandOrientation); + } +} + +/*void UContinuousMovementComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + + if (PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bMoveWithRightHand) || + PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bSnapTurn)) + { + // if we want to change input bindings, we need to setup the player input again to load a different mapping context, + // or assign a different function to an input action. + // This is automatically done by restarting the pawn(calling SetupPlayerInputComponent() directly results in crashes) + // only do this in the editor + #if WITH_EDITOR + VRPawn->Restart(); +#endif + } +}*/ \ No newline at end of file diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp new file mode 100644 index 00000000..5444bd50 --- /dev/null +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Pawn/VRPawnInputConfig.h" + diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp deleted file mode 100644 index 4b32da47..00000000 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ /dev/null @@ -1,193 +0,0 @@ - -#include "Pawn/VRPawnMovement.h" -#include "DrawDebugHelpers.h" - -UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) -{ - CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); - CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); - CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); -} - -void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ - - SetCapsuleColliderToUserSize(); - - FVector PositionChange = GetPendingInputVector(); - - if (NavigationMode == EVRNavigationModes::NAV_WALK) - { - PositionChange.Z = 0.0f; - ConsumeInputVector(); - AddInputVector(PositionChange); - } - - if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) - { - MoveByGravityOrStepUp(DeltaTime); - CheckForPhysWalkingCollision(); - - if(CheckForVirtualMovCollision(PositionChange, DeltaTime)) - { - ConsumeInputVector(); - } - } - - if(NavigationMode == EVRNavigationModes::NAV_NONE) - { - ConsumeInputVector(); - } - - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - LastHeadPosition = HeadComponent->GetComponentLocation(); -} - -bool UVRPawnMovement::CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime) -{ - FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; - FHitResult FHitResultVR; - CapsuleColliderComponent->AddWorldOffset(ProbePosition, true, &FHitResultVR); - if (FVector::Distance(FHitResultVR.Location, CapsuleColliderComponent->GetComponentLocation()) < CapsuleColliderComponent->GetScaledCapsuleRadius()) - { - return true; - } - return false; -} - -void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) -{ - HeadComponent = NewHeadComponent; - CapsuleColliderComponent->SetupAttachment(HeadComponent); - const float HalfHeight = 80.0f; //this is just an initial value to look good in editor - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, HalfHeight); - CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,HalfHeight)); -} - -void UVRPawnMovement::SetCapsuleColliderToUserSize() -{ - float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - HeadComponent->GetComponentLocation().Z); - - if (CharachterSize > MaxStepHeight) - { - float ColliderHeight = CharachterSize - MaxStepHeight; - float ColliderHalfHeight = ColliderHeight / 2.0f; - if (ColliderHalfHeight <= CapsuleRadius) - {//Make the collider to a Sphere - CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); - } - else - {//Make the collider to a Capsule - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); - } - - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); - } - else - { - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); - } -} - -void UVRPawnMovement::CheckForPhysWalkingCollision() -{ - FVector CurrentHeadPosition = HeadComponent->GetComponentLocation(); - FVector Direction = CurrentHeadPosition - LastHeadPosition; - FHitResult FHitResultPhys; - CapsuleColliderComponent->AddWorldOffset(Direction, true, &FHitResultPhys); - - if (FHitResultPhys.bBlockingHit) - { - UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth); - } -} - -void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) -{ - FVector StartLineTraceUnderCollider = CapsuleColliderComponent->GetComponentLocation(); - StartLineTraceUnderCollider.Z -= CapsuleColliderComponent->GetScaledCapsuleHalfHeight(); - FHitResult HitDetailsMultiLineTrace = CreateMultiLineTrace(FVector(0, 0, -1), StartLineTraceUnderCollider, CapsuleColliderComponent->GetScaledCapsuleRadius() / 4.0f, false); - float DistanceDifference = abs(MaxStepHeight - HitDetailsMultiLineTrace.Distance); - //Going up (in Fly and Walk Mode) - if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight)) - { - ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1); - } - //Gravity (only in Walk Mode) - else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.GetActor() == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f))) - { - ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1); - } -} - -void UVRPawnMovement::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction) -{ - VerticalSpeed += VerticalAcceleration * DeltaSeconds; - if (VerticalSpeed*DeltaSeconds < DiffernceDistance) - { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds)); - } - else - { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * DiffernceDistance)); - VerticalSpeed = 0; - } -} - -FHitResult UVRPawnMovement::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility) -{ - //Re-initialize hit info - FHitResult HitDetails = FHitResult(ForceInit); - - FVector End = ((Direction * 1000.f) + Start); - // additional trace parameters - FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, NULL); - TraceParams.bTraceComplex = true; //to use complex collision on whatever we interact with to provide better precision. - TraceParams.bReturnPhysicalMaterial = true; //to provide details about the physical material, if one exists on the thing we hit, to come back in our hit result. - - if (Visibility) - DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1); - - if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams)) - { - if (HitDetails.bBlockingHit) - { - } - } - return HitDetails; -} - -FHitResult UVRPawnMovement::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility) -{ - TArray<FVector> StartVectors; - TArray<FHitResult> OutHits; - FHitResult HitDetailsMultiLineTrace; - HitDetailsMultiLineTrace.Distance = -1.0f;//(Distance=-1) not existing, but to know if this Variable not Initialized(when all Traces not compatible) - - StartVectors.Add(Start); //LineTraceCenter - StartVectors.Add(Start + FVector(0, -Radius, 0)); //LineTraceLeft - StartVectors.Add(Start + FVector(0, +Radius, 0)); //LineTraceRight - StartVectors.Add(Start + FVector(+Radius, 0, 0)); //LineTraceFront - StartVectors.Add(Start + FVector(-Radius, 0, 0)); //LineTraceBehind - - bool IsBlockingHitAndSameActor = true; - bool IsAllNothingHiting = true; - // loop through TArray - for (FVector& Vector : StartVectors) - { - FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility); - OutHits.Add(OutHit); - IsBlockingHitAndSameActor &= (OutHit.GetActor() == OutHits[0].GetActor()); //If all Hiting the same Object, then you are (going up/down) or (walking) - IsAllNothingHiting &= (OutHit.GetActor() == nullptr); //If all Hiting nothing, then you are falling - } - - if (IsBlockingHitAndSameActor || IsAllNothingHiting) - HitDetailsMultiLineTrace = OutHits[0]; - - return HitDetailsMultiLineTrace; -} \ No newline at end of file diff --git a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp index d65ca8ab..3676a790 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp @@ -2,11 +2,15 @@ #include "Pawn/VirtualRealityPawn.h" -#include "GameFramework/InputSettings.h" -#include "GameFramework/PlayerInput.h" +#include "AITypes.h" +#include "Engine/LocalPlayer.h" +#include "GameFramework/PlayerController.h" #include "Pawn/UniversalTrackedComponent.h" #include "Utility/VirtualRealityUtilities.h" -#include "Pawn/VRPawnMovement.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "Camera/CameraComponent.h" +#include "Pawn/VRPawnInputConfig.h" AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -15,7 +19,6 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial bUseControllerRotationPitch = true; bUseControllerRotationRoll = true; BaseEyeHeight = 160.0f; - AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events. SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Origin"))); @@ -32,14 +35,17 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial CapsuleRotationFix->SetUsingAbsoluteRotation(true); CapsuleRotationFix->SetupAttachment(Head); - PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement")); + /*PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement")); PawnMovement->SetUpdatedComponent(RootComponent); - PawnMovement->SetHeadComponent(CapsuleRotationFix); + PawnMovement->SetHeadComponent(CapsuleRotationFix);*/ RightHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Right Hand")); RightHand->ProxyType = ETrackedComponentType::TCT_RIGHT_HAND; RightHand->AttachementType = EAttachementType::AT_FLYSTICK; RightHand->SetupAttachment(RootComponent); + + auto MCRight = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("MC Right")); + MCRight->SetTrackingSource(EControllerHand::Right); LeftHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Left Hand")); LeftHand->ProxyType = ETrackedComponentType::TCT_LEFT_HAND; @@ -48,23 +54,62 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial BasicVRInteraction = CreateDefaultSubobject<UBasicVRInteractionComponent>(TEXT("Basic VR Interaction")); BasicVRInteraction->Initialize(RightHand); + } void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { - Super::SetupPlayerInputComponent(PlayerInputComponent); - if (!PlayerInputComponent) return; - PlayerInputComponent->BindAxis("MoveForward", this, &AVirtualRealityPawn::OnForward); - PlayerInputComponent->BindAxis("MoveRight", this, &AVirtualRealityPawn::OnRight); - PlayerInputComponent->BindAxis("MoveUp", this, &AVirtualRealityPawn::OnUp); - PlayerInputComponent->BindAxis("TurnRate", this, &AVirtualRealityPawn::OnTurnRate); - PlayerInputComponent->BindAxis("LookUpRate", this, &AVirtualRealityPawn::OnLookUpRate); + // simple way of changing the handedness + /*if(bMoveWithRightHand) + { + MovementHand = RightHand; + RotationHand = LeftHand; + IMCMovement = IMCMovementRight; + } else + { + MovementHand = LeftHand; + RotationHand = RightHand; + IMCMovement = IMCMovementLeft; + }*/ + + const APlayerController* PlayerController = Cast<APlayerController>(GetController()); + UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()); + if(!InputSubsystem) + { + UE_LOG(LogTemp,Error,TEXT("InputSubsystem IS NOT VALID")); + } + + InputSubsystem->ClearAllMappings(); - // function bindings for grabbing and releasing - PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AVirtualRealityPawn::OnBeginFire); - PlayerInputComponent->BindAction("Fire", IE_Released, this, &AVirtualRealityPawn::OnEndFire); + // add Input Mapping context + //InputSubsystem->AddMappingContext(IMCMovement,0); + InputSubsystem->AddMappingContext(IMCBase,0); + + UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(PlayerInputComponent); + + // old function bindings for grabbing and releasing + EI->BindAction(InputActions->Fire, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginFire); + EI->BindAction(InputActions->Fire, ETriggerEvent::Completed, this, &AVirtualRealityPawn::OnEndFire); + + // grabbing + EI->BindAction(InputActions->Grab, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginGrab); + EI->BindAction(InputActions->Grab, ETriggerEvent::Completed, this, &AVirtualRealityPawn::OnEndGrab); + + /*// walking + EI->BindAction(InputActions->Move, ETriggerEvent::Triggered, this, &AVirtualRealityPawn::OnBeginMove);*/ + /* + // turning + if(bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode()) + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginSnapTurn); + } else + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Triggered, this, &AVirtualRealityPawn::OnBeginTurn); + } + */ + // bind functions for desktop rotations only on holding down right mouse if (UVirtualRealityUtilities::IsDesktopMode()) { @@ -75,12 +120,12 @@ void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInput PC->bEnableClickEvents = true; PC->bEnableMouseOverEvents = true; } - PlayerInputComponent->BindAction("EnableDesktopRotation", IE_Pressed, this, &AVirtualRealityPawn::StartDesktopRotation); - PlayerInputComponent->BindAction("EnableDesktopRotation", IE_Released, this, &AVirtualRealityPawn::EndDesktopRotation); + /*EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Started, this, &AVirtualRealityPawn::StartDesktopRotation); + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Completed, this, &AVirtualRealityPawn::EndDesktopRotation);*/ } } -void AVirtualRealityPawn::StartDesktopRotation() +/*void AVirtualRealityPawn::StartDesktopRotation() { bApplyDesktopRotation = true; } @@ -88,9 +133,9 @@ void AVirtualRealityPawn::StartDesktopRotation() void AVirtualRealityPawn::EndDesktopRotation() { bApplyDesktopRotation = false; -} +}*/ -void AVirtualRealityPawn::SetCameraOffset() const +/*void AVirtualRealityPawn::SetCameraOffset() const { // this also incorporates the BaseEyeHeight, if set as static offset, // rotations are still around the center of the pawn (on the floor), so pitch rotations look weird @@ -98,9 +143,9 @@ void AVirtualRealityPawn::SetCameraOffset() const FRotator Rotation; GetActorEyesViewPoint(Location, Rotation); CameraComponent->SetWorldLocationAndRotation(Location, Rotation); -} +}*/ -void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() +/*void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() { APlayerController* PC = Cast<APlayerController>(GetController()); if (PC) @@ -110,76 +155,126 @@ void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() FRotator HandOrientation = MouseDirection.ToOrientationRotator(); RightHand->SetWorldRotation(HandOrientation); } -} +}*/ -void AVirtualRealityPawn::OnForward_Implementation(float Value) +/*void AVirtualRealityPawn::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { - //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position - if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) - { - AddMovementInput(RightHand->GetForwardVector(), Value); - } - else if (Head) + Super::PostEditChangeProperty(PropertyChangedEvent); + + FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + + if (PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bMoveWithRightHand) || + PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bSnapTurn)) { - AddMovementInput(Head->GetForwardVector(), Value); + // if we want to change input bindings, we need to setup the player input again to load a different mapping context, + // or assign a different function to an input action. + // This is automatically done by restarting the pawn(calling SetupPlayerInputComponent() directly results in crashes) + // only do this in the editor + #if WITH_EDITOR + Restart(); + #endif } -} +}*/ + -void AVirtualRealityPawn::OnRight_Implementation(float Value) +void AVirtualRealityPawn::OnUp(const FInputActionValue& Value) { + const float MoveValue = Value.Get<FVector2D>().X; + UE_LOG(LogTemp,Warning,TEXT("MoveUp: %f"),MoveValue); //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) { - AddMovementInput(RightHand->GetRightVector(), Value); + AddMovementInput(RightHand->GetUpVector(), MoveValue); } else if (Head) { - AddMovementInput(Head->GetRightVector(), Value); + AddMovementInput(Head->GetUpVector(), MoveValue); } } -void AVirtualRealityPawn::OnUp_Implementation(float Value) +// legacy grabbing +void AVirtualRealityPawn::OnBeginFire(const FInputActionValue& Value) { - //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position - if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) - { - AddMovementInput(RightHand->GetUpVector(), Value); - } - else if (Head) - { - AddMovementInput(Head->GetUpVector(), Value); - } + UE_LOG(LogTemp,Warning,TEXT("BeginFire")); + BasicVRInteraction->BeginInteraction(); } -void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate) +// legacy grabbing +void AVirtualRealityPawn::OnEndFire(const FInputActionValue& Value) { - /* Turning the user externally will make them sick */ - if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + UE_LOG(LogTemp,Warning,TEXT("EndFire")); + BasicVRInteraction->EndInteraction(); +} + +void AVirtualRealityPawn::OnBeginGrab(const FInputActionValue& Value) +{ + UE_LOG(LogTemp,Warning,TEXT("BeginGrab")); +} + +void AVirtualRealityPawn::OnEndGrab(const FInputActionValue& Value) +{ + UE_LOG(LogTemp,Warning,TEXT("EndGrab")); +} + +/*void AVirtualRealityPawn::OnBeginMove(const FInputActionValue& Value) +{ + const FVector ForwardDir = UVirtualRealityUtilities::IsDesktopMode() ? Head->GetForwardVector() : MovementHand->GetForwardVector(); + const FVector RightDir = UVirtualRealityUtilities::IsDesktopMode() ? Head->GetRightVector() : MovementHand->GetRightVector(); + + if (Controller != nullptr) { - AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation); + const FVector2D MoveValue = Value.Get<FVector2D>(); + const FRotator MovementRotation(0, Controller->GetControlRotation().Yaw, 0); + + // Forward/Backward direction + if (MoveValue.X != 0.f) + { + AddMovementInput(ForwardDir, MoveValue.X); + } + + // Right/Left direction + if (MoveValue.Y != 0.f) + { + AddMovementInput(RightDir, MoveValue.Y); + } } - if (UVirtualRealityUtilities::IsDesktopMode()) +}*/ + +/*void AVirtualRealityPawn::OnBeginTurn(const FInputActionValue& Value) +{ + if(UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation) return; + if (Controller != nullptr) { - UpdateRightHandForDesktopInteraction(); + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + AddControllerYawInput(TurnRateFactor * TurnValue.X); + if (UVirtualRealityUtilities::IsDesktopMode()) + { + UpdateRightHandForDesktopInteraction(); + } + } + + if (TurnValue.Y != 0.f) + { + if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + { + AddControllerPitchInput(TurnRateFactor * TurnValue.Y); + SetCameraOffset(); + } + } } } -void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate) +void AVirtualRealityPawn::OnBeginSnapTurn(const FInputActionValue& Value) { - /* Turning the user externally will make them sick */ - if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) { - AddControllerPitchInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation); - SetCameraOffset(); + AddControllerYawInput(SnapTurnAngle); } -} +}*/ -void AVirtualRealityPawn::OnBeginFire_Implementation() -{ - BasicVRInteraction->BeginInteraction(); -} -void AVirtualRealityPawn::OnEndFire_Implementation() -{ - BasicVRInteraction->EndInteraction(); -} diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h similarity index 55% rename from Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h rename to Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h index 298799e5..fb335133 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h @@ -1,73 +1,149 @@ -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/FloatingPawnMovement.h" -#include "Components/CapsuleComponent.h" -#include "Camera/CameraComponent.h" - -#include "VRPawnMovement.generated.h" - -/* - * This Movement component is needed since in VR not only the pawn itself (UpdatedComponent) is moved but also the - * user herself can walk and thereby move the CameraComponent, which can also lead to collisions or e.g. going up steps - * - * The four modes are: - * None: No controller movement is applied and no corrections regarding steps or collisions with walls are done - * Ghost: The same as above but now the Inputs can be used for unconstrained flying (also through objects) - * Fly: The user can fly but not through walls etc. When the user walks against a wall the scene is moved with her to avoid walking through - * The user can also walk up stairs with a maximum step height of MaxStepHeight - * Walk: Additionally to Fly now gravity keeps the user on the floor - */ - -UENUM(BlueprintType) -enum class EVRNavigationModes : uint8 -{ - NAV_NONE UMETA(DisplayName = "None (no controller movement)"), - NAV_GHOST UMETA(DisplayName = "Ghost (flying, also through walls)"), - NAV_FLY UMETA(DisplayName = "Fly (prohibiting collisions)"), - NAV_WALK UMETA(DisplayName = "Walk (gravity and prohibiting collisions)") -}; - -UCLASS() -class RWTHVRTOOLKIT_API UVRPawnMovement : public UFloatingPawnMovement -{ - GENERATED_UCLASS_BODY() - -public: - - virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, - FActorComponentTickFunction* ThisTickFunction) override; - - void SetHeadComponent(USceneComponent* NewHeadComponent); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float MaxStepHeight = 40.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float GravityAcceleration = 981.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float UpSteppingAcceleration = 500.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float CapsuleRadius = 40.0f; - -private: - FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); - FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); - void SetCapsuleColliderToUserSize(); - void CheckForPhysWalkingCollision(); - bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime); - void MoveByGravityOrStepUp(float DeltaSeconds); - void ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction); - //(direction = Down = -1), (direction = Up = 1) - - UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; - UPROPERTY() USceneComponent* HeadComponent = nullptr; - - float VerticalSpeed = 0.0f; - FVector LastHeadPosition; -}; +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/FloatingPawnMovement.h" +#include "Components/CapsuleComponent.h" +#include "Pawn/VirtualRealityPawn.h" + +#include "ContinuousMovementComponent.generated.h" + + +/* + * This Movement component is needed since in VR not only the pawn itself (UpdatedComponent) is moved but also the + * user herself can walk and thereby move the CameraComponent, which can also lead to collisions or e.g. going up steps + * + * The four modes are: + * None: No controller movement is applied and no corrections regarding steps or collisions with walls are done + * Ghost: The same as above but now the Inputs can be used for unconstrained flying (also through objects) + * Fly: The user can fly but not through walls etc. When the user walks against a wall the scene is moved with her to avoid walking through + * The user can also walk up stairs with a maximum step height of MaxStepHeight + * Walk: Additionally to Fly now gravity keeps the user on the floor + */ + +UENUM(BlueprintType) +enum class EVRNavigationModes : uint8 +{ + NAV_NONE UMETA(DisplayName = "None (no controller movement)"), + NAV_GHOST UMETA(DisplayName = "Ghost (flying, also through walls)"), + NAV_FLY UMETA(DisplayName = "Fly (prohibiting collisions)"), + NAV_WALK UMETA(DisplayName = "Walk (gravity and prohibiting collisions)") +}; + + +/** + * + */ +UCLASS(Blueprintable) +class RWTHVRTOOLKIT_API UContinuousMovementComponent : public UFloatingPawnMovement +{ + GENERATED_BODY() + +public: + + UContinuousMovementComponent(const FObjectInitializer& ObjectInitializer); + + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) override; + + virtual void BeginPlay() override; + + void SetHeadComponent(USceneComponent* NewHeadComponent); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float MaxStepHeight = 40.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float GravityAcceleration = 981.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float UpSteppingAcceleration = 500.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float CapsuleRadius = 40.0f; + + + /*Movement Input*/ + UFUNCTION(BlueprintCallable) + void OnBeginMove(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable) + void OnBeginTurn(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable) + void OnBeginSnapTurn(const FInputActionValue& Value); + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UInputMappingContext* IMCMovementRight; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UInputMappingContext* IMCMovementLeft; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UVRPawnInputConfig* InputActions; + + /*Desktop Testing*/ + // the idea is that you have to hold the right mouse button to do rotations + UFUNCTION() + void StartDesktopRotation(); + + UFUNCTION() + void EndDesktopRotation(); + + bool bApplyDesktopRotation = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + bool bMoveWithRightHand = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + bool bSnapTurn = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta=(EditCondition="!bSnapTurn")) + float TurnRateFactor = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta=(EditCondition="bSnapTurn",ClampMin=0,ClampMax=360)) + float SnapTurnAngle = 22.5; + +private: + FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); + FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); + void SetCapsuleColliderToUserSize(); + void CheckForPhysWalkingCollision(); + bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime); + void MoveByGravityOrStepUp(float DeltaSeconds); + void ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction); + //(direction = Down = -1), (direction = Up = 1) + + UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; + UPROPERTY() USceneComponent* HeadComponent = nullptr; + + float VerticalSpeed = 0.0f; + FVector LastHeadPosition; + + + UPROPERTY() + UUniversalTrackedComponent* MovementHand; + + UPROPERTY() + UUniversalTrackedComponent* RotationHand; + + UPROPERTY() + class UInputMappingContext* IMCMovement; + + void SetupInputActions(); + + AVirtualRealityPawn* VRPawn; + + /*virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;*/ + + /** + * Fixes camera rotation in desktop mode. + */ + void SetCameraOffset() const; + void UpdateRightHandForDesktopInteraction(); + +}; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h new file mode 100644 index 00000000..a75687f3 --- /dev/null +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h @@ -0,0 +1,40 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "InputAction.h" +#include "Engine/DataAsset.h" +#include "VRPawnInputConfig.generated.h" + +/** + * + */ +UCLASS() +class RWTHVRTOOLKIT_API UVRPawnInputConfig : public UDataAsset +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* MoveUp; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* DesktopRotation; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Fire; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Grab; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Move; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Turn; + + + +}; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h index c1c0789f..79f7af9b 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h @@ -4,16 +4,15 @@ #include "BasicVRInteractionComponent.h" #include "CoreMinimal.h" -#include "GameFramework/DefaultPawn.h" #include "UniversalTrackedComponent.h" -#include "VRPawnMovement.h" #include "VirtualRealityPawn.generated.h" +class UCameraComponent; class ULiveLinkComponentController; /** * */ -UCLASS() +UCLASS(Abstract) class RWTHVRTOOLKIT_API AVirtualRealityPawn : public APawn { GENERATED_BODY() @@ -21,15 +20,34 @@ public: AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer); /* Proxy */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Head; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* RightHand; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* LeftHand; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* Head; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* RightHand; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* LeftHand; /* Interaction */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") + UBasicVRInteractionComponent* BasicVRInteraction; /* Movement */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement; + /*UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") + UVRPawnMovement* PawnMovement;*/ + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + bool bMoveWithRightHand = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + bool bSnapTurn = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement", meta=(EditCondition="!bSnapTurn")) + float TurnRateFactor = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement", meta=(EditCondition="bSnapTurn",ClampMin=0,ClampMax=360)) + float SnapTurnAngle = 22.5; /** Workaround dummy component to prevent the Capsule from rotating in the editor, if LiveLink tracking is being used. * This happens due to the rotation of the Capsule being set only while in Play Mode (instead of using e.g. absolute rotation). @@ -38,32 +56,79 @@ public: * The dummy seems to fix this, because its absolute rotation just catches all parent rotations and prevents them from * overriding any of the capsules'. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") USceneComponent* CapsuleRotationFix; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") float BaseTurnRate = 45.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + USceneComponent* CapsuleRotationFix; + /* CameraComponent */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera") UCameraComponent* CameraComponent; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera") + UCameraComponent* CameraComponent; + protected: virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override; /* Movement */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnForward(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnRight(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnUp(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnTurnRate(float Rate); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnLookUpRate(float Rate); + UFUNCTION(BlueprintCallable, Category = "Pawn|Movement") + void OnUp(const FInputActionValue& Value); /* Interaction */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnBeginFire(); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnEndFire(); + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginFire(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnEndFire(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginGrab(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnEndGrab(const FInputActionValue& Value); + + /* + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginMove(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginTurn(const FInputActionValue& Value); + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginSnapTurn(const FInputActionValue& Value); + */ + + /* Input */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pawn|Input") + class UInputMappingContext* IMCBase; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pawn|Input") + class UVRPawnInputConfig* InputActions; + /*Desktop Testing*/ // the idea is that you have to hold the right mouse button to do rotations - UFUNCTION() void StartDesktopRotation(); - UFUNCTION() void EndDesktopRotation(); - bool bApplyDesktopRotation = false; + /*UFUNCTION() + void StartDesktopRotation(); + + UFUNCTION() + void EndDesktopRotation(); + + bool bApplyDesktopRotation = false;*/ + + /** + * Fixes camera rotation in desktop mode. + */ + /*void SetCameraOffset() const; + void UpdateRightHandForDesktopInteraction();*/ + +private: + /*UPROPERTY() + UUniversalTrackedComponent* MovementHand; + + UPROPERTY() + UUniversalTrackedComponent* RotationHand; + + UPROPERTY() + class UInputMappingContext* IMCMovement;*/ + + /*virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;*/ - void SetCameraOffset() const; - void UpdateRightHandForDesktopInteraction(); }; diff --git a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs index 7a3abd66..8345d954 100644 --- a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs +++ b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs @@ -28,7 +28,8 @@ public class RWTHVRToolkit : ModuleRules "DeveloperSettings", "HTTP", "LiveLink", - "LiveLinkInterface" + "LiveLinkInterface", + "EnhancedInput" } ); -- GitLab