スパイダーフックの実装
スパイダーフックの実装
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(AudioSource))]
public class PlayerController : MonoBehaviour
{
public float walkSpeed;
public float runSpeed;
private Vector3 movement;
private CharacterController characterController;
private AudioSource audioSource;
public GameObject FPSCamera;
private Vector3 moveDir = Vector3.zero;
private float gravity = 9.8f;
public float jumpPower;
public AudioClip jumpSound;
private float runStepLengthen = 0.7f;
private float stepInterval = 10f;
private float stepCycle;
private float nextStep;
private float speed;
private bool isWalking;
public AudioClip stepSound;
private Vector3 initialCameraPosition;
private float timer = 0;
private float bobbingSpeed = 10f;
private float walkBobbing = 0.04f;
private float runBobbing = 0.07f;
// ★追加(スパイダーフック)
private enum State
{
Normal,
ThrowingHook,
FlyingPlayer
}
// ★追加(スパイダーフック)
public float hookRange = 100f;
public Transform hookTargetMark;
public Transform hookShot;
private Vector3 hookPoint;
private float hookShotSize;
private State state;
private void Start()
{
characterController = GetComponent<CharacterController>();
audioSource = GetComponent<AudioSource>();
initialCameraPosition = FPSCamera.transform.localPosition;
// ★追加(スパイダーフック)
state = State.Normal;
hookShot.gameObject.SetActive(false);
hookTargetMark.gameObject.SetActive(false);
}
private void Update()
{
// ★改良&追加(スパイダーフック)
// (テクニック)
// enumとswitch文を組み合わせることで、『この状態(モード)の時は、このメソッドを実行する』という仕組みを作れる。
switch(state)
{
default:
case State.Normal: // ノーマルモードの時
PlayerMove();
ProgressStepCycle(speed);
HeadBob();
HookStart();
break;
case State.ThrowingHook: // フック投げモードの時
HookThrow();
break;
case State.FlyingPlayer: // プレーヤーが空中移動の時
HookFlyingMovement();
break;
}
}
void PlayerMove()
{
float moveH = Input.GetAxis("Horizontal");
float moveV = Input.GetAxis("Vertical");
movement = new Vector3(moveH, 0, moveV);
Vector3 desiredMove = FPSCamera.transform.forward * movement.z + FPSCamera.transform.right * movement.x;
moveDir.x = desiredMove.x * 5f;
moveDir.z = desiredMove.z * 5f;
if (Input.GetKey(KeyCode.LeftShift))
{
characterController.Move(moveDir * Time.deltaTime * runSpeed);
isWalking = false;
speed = runSpeed;
}
else
{
characterController.Move(moveDir * Time.deltaTime * walkSpeed);
isWalking = true;
speed = walkSpeed;
}
if (characterController.isGrounded)
{
if (Input.GetKeyDown(KeyCode.Space))
{
moveDir.y = jumpPower;
audioSource.clip = jumpSound;
audioSource.Play();
}
}
else
{
moveDir.y -= gravity * Time.deltaTime;
}
}
void PlayStepSound()
{
if (!characterController.isGrounded)
{
return;
}
audioSource.clip = stepSound;
audioSource.PlayOneShot(audioSource.clip);
}
void ProgressStepCycle(float speed)
{
if (characterController.velocity.sqrMagnitude > 0 && (movement.x != 0 || movement.z != 0))
{
stepCycle += (characterController.velocity.magnitude + (speed * (isWalking ? 1f : runStepLengthen))) * Time.deltaTime;
}
if (!(stepCycle > nextStep))
{
return;
}
nextStep = stepCycle + stepInterval;
PlayStepSound();
}
void HeadBob()
{
Vector3 newCameraPosition;
float bobbingAmount;
if (characterController.velocity.sqrMagnitude > 0 && (movement.x != 0 || movement.z != 0))
{
timer += (Time.deltaTime * bobbingSpeed);
bobbingAmount = isWalking ? walkBobbing : runBobbing;
FPSCamera.transform.localPosition = new Vector3(0, initialCameraPosition.y + Mathf.Sin(timer) * bobbingAmount, 0);
newCameraPosition = FPSCamera.transform.localPosition;
}
else
{
timer = 0;
newCameraPosition = FPSCamera.transform.localPosition;
}
FPSCamera.transform.localPosition = newCameraPosition;
}
// ★追加(スパイダーフック)
void HookStart()
{
// マウスの右ボタンを推した時
if(Input.GetMouseButtonDown(1))
{
RaycastHit hit;
if(Physics.Raycast(FPSCamera.transform.position, FPSCamera.transform.forward, out hit, hookRange))
{
// Rayで特定した位置にターゲットマーク(目印)を移動させる。
hookTargetMark.position = hit.point;
hookPoint = hit.point;
state = State.ThrowingHook;
hookShotSize = 0f;
hookTargetMark.gameObject.SetActive(true);
hookShot.gameObject.SetActive(true);
}
}
}
// ★追加(スパイダーフック)
void HookFlyingMovement()
{
Vector3 moveDir = (hookPoint - transform.position).normalized;
// (テクニック)
// 現在地と目的地の距離が遠いほど移動速度が早い(近くなるにつれて減速)
float flyingSpeed = Vector3.Distance(transform.position, hookPoint) * 2f;
characterController.Move(moveDir * flyingSpeed * Time.deltaTime);
// 右クリックでスパイダーフックの解除
if(Input.GetMouseButtonDown(1))
{
// ノーマルモードに移行する
state = State.Normal;
hookTargetMark.gameObject.SetActive(false);
hookShot.gameObject.SetActive(false);
}
// 目標地点の近くまで来るとフックショットを自動的に隠す
if(Vector3.Distance(transform.position, hookPoint) < 2f)
{
hookShot.gameObject.SetActive(false);
}
}
// ★追加(スパイダーフック)
void HookThrow()
{
hookShot.LookAt(hookPoint);
float hookShotSpeed = 50f;
hookShotSize += hookShotSpeed * Time.deltaTime;
hookShot.localScale = new Vector3(1, 1, hookShotSize);
if(hookShotSize >= Vector3.Distance(transform.position, hookPoint))
{
// 空中移動モードに移行(空中移動を開始する)
state = State.FlyingPlayer;
}
}
}
【2019版】X_Mission(基礎/全51回)
他のコースを見るスパイダーフックの実装
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(AudioSource))]
public class PlayerController : MonoBehaviour
{
public float walkSpeed;
public float runSpeed;
private Vector3 movement;
private CharacterController characterController;
private AudioSource audioSource;
public GameObject FPSCamera;
private Vector3 moveDir = Vector3.zero;
private float gravity = 9.8f;
public float jumpPower;
public AudioClip jumpSound;
private float runStepLengthen = 0.7f;
private float stepInterval = 10f;
private float stepCycle;
private float nextStep;
private float speed;
private bool isWalking;
public AudioClip stepSound;
private Vector3 initialCameraPosition;
private float timer = 0;
private float bobbingSpeed = 10f;
private float walkBobbing = 0.04f;
private float runBobbing = 0.07f;
// ★追加(スパイダーフック)
private enum State
{
Normal,
ThrowingHook,
FlyingPlayer
}
// ★追加(スパイダーフック)
public float hookRange = 100f;
public Transform hookTargetMark;
public Transform hookShot;
private Vector3 hookPoint;
private float hookShotSize;
private State state;
private void Start()
{
characterController = GetComponent<CharacterController>();
audioSource = GetComponent<AudioSource>();
initialCameraPosition = FPSCamera.transform.localPosition;
// ★追加(スパイダーフック)
state = State.Normal;
hookShot.gameObject.SetActive(false);
hookTargetMark.gameObject.SetActive(false);
}
private void Update()
{
// ★改良&追加(スパイダーフック)
// (テクニック)
// enumとswitch文を組み合わせることで、『この状態(モード)の時は、このメソッドを実行する』という仕組みを作れる。
switch(state)
{
default:
case State.Normal: // ノーマルモードの時
PlayerMove();
ProgressStepCycle(speed);
HeadBob();
HookStart();
break;
case State.ThrowingHook: // フック投げモードの時
HookThrow();
break;
case State.FlyingPlayer: // プレーヤーが空中移動の時
HookFlyingMovement();
break;
}
}
void PlayerMove()
{
float moveH = Input.GetAxis("Horizontal");
float moveV = Input.GetAxis("Vertical");
movement = new Vector3(moveH, 0, moveV);
Vector3 desiredMove = FPSCamera.transform.forward * movement.z + FPSCamera.transform.right * movement.x;
moveDir.x = desiredMove.x * 5f;
moveDir.z = desiredMove.z * 5f;
if (Input.GetKey(KeyCode.LeftShift))
{
characterController.Move(moveDir * Time.deltaTime * runSpeed);
isWalking = false;
speed = runSpeed;
}
else
{
characterController.Move(moveDir * Time.deltaTime * walkSpeed);
isWalking = true;
speed = walkSpeed;
}
if (characterController.isGrounded)
{
if (Input.GetKeyDown(KeyCode.Space))
{
moveDir.y = jumpPower;
audioSource.clip = jumpSound;
audioSource.Play();
}
}
else
{
moveDir.y -= gravity * Time.deltaTime;
}
}
void PlayStepSound()
{
if (!characterController.isGrounded)
{
return;
}
audioSource.clip = stepSound;
audioSource.PlayOneShot(audioSource.clip);
}
void ProgressStepCycle(float speed)
{
if (characterController.velocity.sqrMagnitude > 0 && (movement.x != 0 || movement.z != 0))
{
stepCycle += (characterController.velocity.magnitude + (speed * (isWalking ? 1f : runStepLengthen))) * Time.deltaTime;
}
if (!(stepCycle > nextStep))
{
return;
}
nextStep = stepCycle + stepInterval;
PlayStepSound();
}
void HeadBob()
{
Vector3 newCameraPosition;
float bobbingAmount;
if (characterController.velocity.sqrMagnitude > 0 && (movement.x != 0 || movement.z != 0))
{
timer += (Time.deltaTime * bobbingSpeed);
bobbingAmount = isWalking ? walkBobbing : runBobbing;
FPSCamera.transform.localPosition = new Vector3(0, initialCameraPosition.y + Mathf.Sin(timer) * bobbingAmount, 0);
newCameraPosition = FPSCamera.transform.localPosition;
}
else
{
timer = 0;
newCameraPosition = FPSCamera.transform.localPosition;
}
FPSCamera.transform.localPosition = newCameraPosition;
}
// ★追加(スパイダーフック)
void HookStart()
{
// マウスの右ボタンを推した時
if(Input.GetMouseButtonDown(1))
{
RaycastHit hit;
if(Physics.Raycast(FPSCamera.transform.position, FPSCamera.transform.forward, out hit, hookRange))
{
// Rayで特定した位置にターゲットマーク(目印)を移動させる。
hookTargetMark.position = hit.point;
hookPoint = hit.point;
state = State.ThrowingHook;
hookShotSize = 0f;
hookTargetMark.gameObject.SetActive(true);
hookShot.gameObject.SetActive(true);
}
}
}
// ★追加(スパイダーフック)
void HookFlyingMovement()
{
Vector3 moveDir = (hookPoint - transform.position).normalized;
// (テクニック)
// 現在地と目的地の距離が遠いほど移動速度が早い(近くなるにつれて減速)
float flyingSpeed = Vector3.Distance(transform.position, hookPoint) * 2f;
characterController.Move(moveDir * flyingSpeed * Time.deltaTime);
// 右クリックでスパイダーフックの解除
if(Input.GetMouseButtonDown(1))
{
// ノーマルモードに移行する
state = State.Normal;
hookTargetMark.gameObject.SetActive(false);
hookShot.gameObject.SetActive(false);
}
// 目標地点の近くまで来るとフックショットを自動的に隠す
if(Vector3.Distance(transform.position, hookPoint) < 2f)
{
hookShot.gameObject.SetActive(false);
}
}
// ★追加(スパイダーフック)
void HookThrow()
{
hookShot.LookAt(hookPoint);
float hookShotSpeed = 50f;
hookShotSize += hookShotSpeed * Time.deltaTime;
hookShot.localScale = new Vector3(1, 1, hookShotSize);
if(hookShotSize >= Vector3.Distance(transform.position, hookPoint))
{
// 空中移動モードに移行(空中移動を開始する)
state = State.FlyingPlayer;
}
}
}
スパイダーフックの実装