Alternativ: Skyd med pistolen

3D GameKit # 6

I Unity3D gamekittet findes der en model for et våben. Vi kan gøre det muligt at skyde i spillet ved at bruge nogle af disse præfabrikerede gestande, samt kode lidt selv og lave noget opsætning.

https://www.youtube.com/watch?v=0yCdnExn1pY&feature=youtu.be

Vi ændrer Playerinput.cs

using UnityEngine.Events;
...
    public UnityEvent OnAimActivate;
    public UnityEvent OnAimInactivate;
...
    protected bool m_Aim;
...
 public bool Aim
    {
        get { return m_Aim && !playerControllerInputBlocked && !m_ExternalInputBlocked; }
    }
...

Derefter laver vi et script

CameraAim.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;

public class CameraAim : CinemachineExtension {

    public Vector3 Offset;

    protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) {
        if (stage == CinemachineCore.Stage.Aim) {
            state.PositionCorrection += state.FinalOrientation * Offset;
        }
    }
}

Tilføjet dette script til KeyboardAndMouseFreeLook objektets komponent "Cinemachine Free Look" extension og disable det. Giv det en offset på 0.16 X og 3.08 Z

Derefter tilføj til CameraSettings.cs

public void ToggleAim(bool b) {
   var aim = Current.GetComponent<CameraAim>();
   aim.enabled = b;
}

i player input smider vi også

void Update()
    {
    bool tempAim = m_Aim;
    m_Aim = Input.GetButton("Fire2");
    if(m_Aim != tempAim) {
        if(m_Aim) OnAimActivate.Invoke();
        else OnAimInactivate.Invoke();
    }
...
}

Vælg derefter Playerinput komponentet i spillet og tilføj CameraSettings ToggleAim funktino til OnAimActivate og OnAimDeactive med true og false bools parametre

Så skal vi tilføje et canvas og et image hvor vi importerer dette billede https://www.clipartmax.com/download/m2H7K9i8m2A0d3A0_crosshair-aim-shoot-target-goal-hit-comments-crosshairs-vector/
og vælger texturetype UI and Sprite og trykker apply og sætter det på

Derefter tilføjer vi også vores Crosshair image til Player input activate funktioner hvor vi slår game object set active til og fra

Så finder vi Ellens Animator controller og tilføjer et nyt boolean parameter IsAiming samt laver en substatemachine der hedder RangedCombatSM

I RangedCombatSM smider vi EllenGunIdle og EllenGunShoot animationerne ind (19:30)

Så laver vi en transition fra EllenGunIdle til Exit og sætter IsAiming false som condition

Så laver vi en transition fra EllenGunIdle til EllenGunShoot med MeleeAttack som trigger og uden exit time
Vi laver også en transition fra EllenGunShoot til EllenGunIdle

Så går vi ud til baselayer og laver en transition fra IdleSM og LocomotionSM til RangedCombatSM med IsAiming true som condition
Vi laver også en transition fra RangedCombatSM til LocomotionSM

I PlayerController.cs tilføjer vi

...
void TimeoutToIdle()
        {
            bool inputDetected = IsMoveInput || m_Input.Attack || m_Input.Aim || m_Input.JumpInput;
..

public void ToggleAim(bool b) {
  m_Animator.SetBool("IsAiming", b);
}

Derefter tilføjer vi transitioner i LocomotionSM fra Idle og Locomotion til Exit med exitTime disabled og IsAiming true som condition

Så laver vi et nyt script IKController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IKController : MonoBehaviour {

    public float MaxAngle = 100f;
    public Vector3 LookTarget;
    public float Weight;
    public float BodyWeight;
    public float HeadWeight;

    Animator _animator;

    private void Awake() {
        _animator = GetComponent<Animator>();
    }

    private void OnAnimatorIK(int layerIndex) {
        _animator.SetLookAtWeight(Weight, BodyWeight, HeadWeight);

        if (Weight == 0) return;

        float deltaAngle = Vector3.SignedAngle(transform.forward, LookTarget - transform.position, transform.up);
        if(Mathf.Abs(deltaAngle) > MaxAngle) {
            transform.Rotate(new Vector3(0, deltaAngle, 0), Space.Self);
        }
        _animator.SetLookAtPosition(LookTarget);
    }
}

Dette script sætter vi på Ellen

I RangedCombatSM tilføjer vi et nyt behaviour script til EllenGunIdle som skal hedde AimIKSMB

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AimIKSMB : StateMachineBehaviour {

    public Vector3 Direction;
    public float Distance;
    IKController _controller;

     // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        _controller = animator.gameObject.GetComponent<IKController>();
        if(_controller == null) return;

        _controller.LookTarget = Camera.main.transform.position + Camera.main.transform.TransformDirection(Direction.normalized) * Distance;
        _controller.Weight = 1;
        _controller.BodyWeight = 1;
        _controller.HeadWeight = 1;
    }

    // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        if(_controller == null) return;
        _controller.LookTarget = Camera.main.transform.position + Camera.main.transform.TransformDirection(Direction.normalized) * Distance;
    }

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        if(_controller == null) return;
        _controller.Weight = 0;
        _controller.BodyWeight = 0;
        _controller.HeadWeight = 0;
    }
}

Tilføj også dette script til EllenGunShoot tilstanden

Derefter skal vi lave Ellen humanoid
So to make this crystal clear for anyone else tying to get this working: (1) select Models > Characters > Ellen > Ellen, (2) Change Inspector > Import settings > Rig > Animation type: Humanoid and Avatar definition: Create from this model, (3) Apply, (4) select Art > Animations > Animation clips > Characters > Ellen @EllenCombo1, (5) Shift+click @EllenWalkForwardsLanding, (6) Change Inspector > Import settings > Rig > Animation type: Humanoid and Avatar definition: Copy from other avatar, (7) Drag Models > Characters > Ellen > EllenAvator to Inspector > Rig > Source, (8) Apply. That should do it though I have read some of the animations will need fixing but for me at least that gets Ellen humanoid. Why the character in the kit does not come this way be default is a question the folks at Unity should ask themselves.

Derefter finder vi PistolRoot på Ellen og tilføjer RangeWeapon og FixedUpdateFollow
Pistol objektet aktiverer vi og fjerner vi de 2 scripts der er og sætter dens Rotation til -74.5, -84, 80 og position til 0,0.014, -0.058
Vi sætter PistolRoot Position til 0.55,1.22,0.01 og Rotation til -180,-9,-1.52 og Scale til 1,1,1 og tilføjer EllenRightHand_Attach til ToFollow på scriptet
RangeWeapon muzzle offset sætter vi til 0.18,0.06,-0.03

I playercontroller.cs

public RangeWeapon rangedWeapon;
...

protected IKController m_Ik;

...
void OnEnable() {
...
            rangedWeapon.gameObject.SetActive(false);
...

    void Awake()
        {
            m_Ik = GetComponent<IKController>();
...

        public void RangedAttackShoot() {
            rangedWeapon.Attack(m_Ik.LookTarget);
            m_InAttack = true;
        }

        public void RangedAttackEnd() {
            m_InAttack = false;
        }

På EllenGunShoot animationen laver vi et nyt event 002.6% inde kaldet RangedAttackShoot og et event 074.3% inde kaldet RangedAttackEnd
tryk derefter apply på animationen

Derefter laver vi et nyt script på RangedCombatSM som skal hedde ToggleRangedWeapon

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Gamekit3D;

public class ToggleRangedWeapon : StateMachineBehaviour {

    // OnStateEnter is called before OnStateEnter is called on any state inside this state machine
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        PlayerController pc = animator.GetComponent<PlayerController>();
        pc.rangedWeapon.gameObject.SetActive(true);
    }

    // OnStateExit is called before OnStateExit is called on any state inside this state machine
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        PlayerController pc = animator.GetComponent<PlayerController>();
        pc.rangedWeapon.gameObject.SetActive(false);
    }
}

I PlayerControllerEditor.cs

...
        SerializedProperty m_MeleeWeaponProp;
        SerializedProperty m_RangedWeaponProp;
...
        GUIContent m_MeleeWeaponContent = new GUIContent("Melee Weapon", "Used for damaging enemies when Ellen swings her staff.");
        GUIContent m_RangedWeaponContent = new GUIContent("Ranged Weapon", "Used for damaging enemies when Ellen shoots her gun.");
...
            m_MeleeWeaponProp = serializedObject.FindProperty("meleeWeapon");
            m_RangedWeaponProp = serializedObject.FindProperty("rangedWeapon");
...
                EditorGUILayout.PropertyField(m_MeleeWeaponProp, m_MeleeWeaponContent);
                EditorGUILayout.PropertyField(m_RangedWeaponProp, m_RangedWeaponContent);
                EditorGUILayout.PropertyField(m_CameraSettingsProp, m_CameraSettingsContent);
...

Sæt pistol root som ranged weapon

Nu skal vi finde EnergyGrenade prefabbet og lægge det i vores Scene hvor vi omdøber den til PistolProjectile og giver den scale 0.17,0.17,0.17
Så fjerner vi Grenadier scriptet og sætter Spit script på i stedet.
Shot type sættes til MOST_DIRECT
Projectile Speed 50
Damage amount 1
Damage Mask Enemy
Explosion Radius 1
Explosion Timer -1
Vælg VFX -> Weapons -> HitParticle som Explosion VFX

Fjern Sphere collider fra PistolProjectile barn Sphere
Men smid en sphere collider på pistol projectile med radius 0.17
Fjern Audio
Reset Position

Lav Pistol Projectile til en Prefab

Mangler resten af tutorial fra 1:05