http://answers.unity3d.com/questions/212189/camera-shake.html
I've followed the question's answer above to try and get a camera shake working for my first person camera. But I've tried to modify it so that the camera shakes from an invisible collision box.
So far my camera shake script looks like this;
public bool Shaking;
private float ShakeDecay;
private float ShakeIntensity;
private Vector3 OriginalPos;
private Quaternion OriginalRot;
void Start()
{
Shaking = false;
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
DoShake();
Debug.Log("The camera trigger has hit");
}
}
void Update ()
{
if(ShakeIntensity > 0)
{
transform.position = OriginalPos + Random.insideUnitSphere * ShakeIntensity;
transform.rotation = new Quaternion(OriginalRot.x + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.y + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.z + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.w + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f);
ShakeIntensity -= ShakeDecay;
}
else if (Shaking)
{
Shaking = false;
}
}
void OnGUI() {
if (GUI.Button(new Rect(10, 200, 50, 30), "Shake"))
DoShake();
//Debug.Log("Shake");
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}
I know the code works 100% via the gui button. This script is attached to the camera on the first person controller. An invisible collision box with the tag ShakeTrigger is in the game. However, the debug log doesn't get called at all and I'm unsure why.
If anyone needs any more information just let me know.
Thanks in advance :)
If the script is attached to your camera, then OnTriggerEnter is looking at the camera for a trigger call, not the collision box.
One thing you could do is stick the OnTriggerEnter into a new script and put that inside the collision box. Then have that do a SendMessage along these lines:
GameObject.Find("Camera").SendMessage("DoShake");
EDIT: To answer Jerdak's questions.
The code bellow would be within the TriggerBox:
void Start()
{
...
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
GameObject.Find("Camera").SendMessage("DoShake");
Debug.Log("The camera trigger has hit");
}
}...
and this would be within the Camera:
void Start()
{
...
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}...
This way, triggerbox is responsible for detecting triggers and only ever sends a message to the camera when right kind of object goes through it. The camera is then responsible for doing the shaking.
To shake your camera on the collision or Trigger you need to first Make function of Your Shake that you can also call from other scripts
Something like
public class ShakeCamera : MonoBehavior
{
public bool canShake;
private void Update()
{
if(canShake)
DoShake()
}
public void DoShake()
{
// Shake Logic
}
public void StartShake()
{
canShake = true;
}
public void StopShake()
{
canShake = false;
}
}
And from your other script whenever you trigger the target object you can call it like this
public class TriggerScript: MonoBehavior
{
public ShakeCamera shakeCamera;
private void Start()
{
shakeCamera = FindObjectOfType<ShakeCamera>();
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StartShake();
}
}
void OnTriggerExit(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StopShake();
}
}
}
I am attaching one reference video for you, May be it would help you in better camera shake. Hope it was helpful.
You can refer to this video I made https://youtu.be/9X_JXexwfR4
If you have set up rigidbody then change the interpolate from none to interpolate.
Related
So . I work at a 2d game in unity and I have some problems with some mechanics and i really need some help from you guys. All I want to do is to make my character hand to stop and grab a object. When I press E the animation of the hand start and have a collider for each frame, in case that the hand collide with a closer object, the animation to stop at that frame. I have a week since I try to figure it out. If you want to help me we can do it on discord. I will put the codes here, maybe the reason that I can't to what I want to do is very clear and I dont see it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class showHide : MonoBehaviour
{
[SerializeField]
GameObject hand;
private Animator freeze;
public bool touch = false;
private bool ok = true;
// Start is called before the first frame update
void Start()
{
freeze = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && ok == true)
{
freeze.Play("Follower");
StartCoroutine(show());
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
StartCoroutine(colliderShow());
}
}
private IEnumerator show()
{
ok = false;
hand.SetActive(true);
yield return new WaitForSeconds(4f);
hand.SetActive(false);
ok = true;
}
private IEnumerator colliderShow()
{
touch = true;
print(touch);
yield return new WaitForSeconds(4f);
touch = false;
print(touch);
}
private void FreezeAniamtion()
{
freeze.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
freeze.speed = 1f;
}
}
This is the code that activate and deactivate my hand object , including the animation .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class facingScript : MonoBehaviour
{
private SpriteRenderer render;
private Camera mainCam;
private Vector3 mousePos;
private Animator aniHand;
public bool atinge;
void Start()
{
mainCam = Camera.main;
render = GetComponent<SpriteRenderer>();
aniHand = GetComponent<Animator>();
gameObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
mousePos = Input.mousePosition;
mousePos = mainCam.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, mainCam.transform.position.z - transform.position.z)
);
Vector3 rotation = mousePos - transform.position;
if (rotation.x > 0)
render.flipY = true;
else
render.flipY = false;
if (atinge == false)
aniHand.speed = 1f;
Debug.Log(aniHand.speed);
}
// Collision with objects
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
atinge = true;
aniHand.speed = 0f;
StartCoroutine(freezingTime());
}
}
private IEnumerator freezingTime()
{
yield return new WaitForSeconds(4f);
atinge = false;
}
// No collision with any object
private void FreezeAnimation()
{
aniHand.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
aniHand.speed = 1f;
}
}
This is the hand component script . When the hand don't collide with anything the object is deactivated . The FreezeAnimation() is a event at the last frame. I tryed to do a collider animation for the showHide component that will be exact with the hand animation collider for each frame to check if the hand collide in the showHide script and if it collide to have a 4 seconds of waiting with the hand at that position.
I really tryed my best but I really can't figure it out.
TL;DR Clients can control only their player objects with keyboard, but all players with gamepad (using Netcode for Game Objects, Unity Input System and a PS4 Controller)
I am working on a multiplayer game and I am relatively new to multiplayer programming. I am using Netcode for GameObjects.
I am using Unity Input System for handling inputs and I created 2 action maps currently. One for movements (with keyboard and gamepad) and one for attacking (with keyboard, gamepad and mouse).
I am trying to move the players in a Server Authoritative way; thus, I am using Server RPCs for handling movements.
The issue I am having is that, when I play with a Gamepad (PS4 controller), one of the clients can control the others. However, it works perfectly with Keyboard actions.
The code I'm using for the player movement is below:
[RequireComponent(typeof(Rigidbody))]
public class PlayerMovement : NetworkBehaviour
{
[SerializeField] private Rigidbody _rb;
[SerializeField] private float movementSpeed = 10f;
[SerializeField] private float jumpSpeed = 8f;
Vector3 _movementVector;
private PlayerInputActions playerInputActions;
private PlayerInputActions PlayerInputActions
{
get
{
if(playerInputActions != null)
{
return playerInputActions;
}
return playerInputActions = new PlayerInputActions();
}
}
public override void OnNetworkSpawn()
{
if(!IsOwner) { return; }
PlayerInputActions.PlayerMovement.Movement.performed += ctx => SetMovement(ctx.ReadValue<Vector2>());
PlayerInputActions.PlayerMovement.Movement.canceled += ctx => SetMovement(Vector2.zero);
PlayerInputActions.PlayerMovement.Jump.performed += PerformJump;
}
public override void OnNetworkDespawn()
{
if (!IsOwner) { return; }
PlayerInputActions.PlayerMovement.Movement.performed -= ctx => SetMovement(ctx.ReadValue<Vector2>());
PlayerInputActions.PlayerMovement.Movement.canceled -= ctx => SetMovement(Vector2.zero);
PlayerInputActions.PlayerMovement.Jump.performed -= PerformJump;
}
private void OnEnable() => PlayerInputActions.Enable();
private void OnDisable() => PlayerInputActions.Disable();
private void SetMovement(Vector2 inputVector) => _movementVector = new Vector3(inputVector.x, 0.0f, 0.0f);
private void PerformJump(InputAction.CallbackContext obj)
{
if (!IsOwner) { return; }
Vector3 jumpVector = Vector3.up;
HandleJumpServerRpc(jumpVector);
}
// Update is called once per frame
void FixedUpdate()
{
if(!IsLocalPlayer) { return; }
HandleMovement();
}
private void HandleMovement()
{
if (!IsOwner) { return; }
HandleMovementServerRpc(_movementVector);
}
#region Server
[ServerRpc]
private void HandleMovementServerRpc(Vector3 movementVector)
{
if(Vector3.Distance(movementVector, Vector3.zero) > 0.000001f)
{
Debug.Log($"Owner ID: {OwnerClientId}");
_rb.MovePosition(transform.position + movementVector * Time.deltaTime * movementSpeed);
}
}
[ServerRpc]
private void HandleJumpServerRpc(Vector3 jumpVector)
{
if (_rb.velocity.y == 0f)
{
_rb.AddForce(jumpVector * jumpSpeed, ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
Debug.Log($"Collided with: {collision.gameObject.name}");
}
#endregion
}
And following is the Action Map I created for movement:
I thought the issue is with the Unity Input System event subscriptions, but I could not find a fix so far for it.
Any help is appreciated!
I'm not sure if this will work but I did
public override void OnNetworkSpawn(){
if (!IsOwner) Destroy(this);
}
I'm trying to implement a system with platforms and OntriggerExit that spawn new platforms. The objects that contain the trigger function are children to said platforms. I.e, I have 4 triggers as children in each platform.
I'm trying to use the trigger object's global position to instantiate the new platform but it keeps doing so according to the prent's position.
For instance, the parent (platform) is at (0,0,0). The trigger, child to the platform, is at (-15,0,0) globally. I try to add (-35,0,0) and it instantiates at (-35,0,0) instead of (-50,0,0).
I do this with a static value. I obtain the Trigger position when OnTriggerExit() and then use it on a different script as follows:
public class TriggerExitN : MonoBehaviour
{
public delegate void ExitAction();
public static event ExitAction OnChunkExitedN;
public static Vector3 positionN;
private bool exited = false;
private void OnTriggerExit(Collider other)
{
Player player = other.GetComponent<Player>();
if (player != null)
{
if (!exited)
{
exited = true;
OnChunkExitedN();
positionN = gameObject.transform.position;
}
}
}
}
If I debug positionN here it returns (-15,0,0) as expected.
Then the platform generator:
public class MapGenerator : MonoBehaviour
{
public GameObject[] mapprefabs;
public GameObject[] startprefabs;
private List<GameObject> platformslist = new List<GameObject>();
public Vector3 spawnOrigin;
private Vector3 spawnPosition;
// Start is called before the first frame update
void Start()
{
platformslist.Add(startprefabs[0]);
}
void OnEnable()
{
TriggerExitN.OnChunkExitedN += PickAndSpawnN;
}
private void OnDisable()
{
TriggerExitN.OnChunkExitedN -= PickAndSpawnN;
}
void PickAndSpawnN()
{
spawnPosition = TriggerExitN.positionN + new Vector3(-35f, 0, 0f);
foreach (GameObject platform in platformslist)
if(platform.transform.position == spawnPosition)
{
return;
}
else
{
continue;
}
GameObject objectFromChunk = mapprefabs[Random.Range(0, mapprefabs.Length)];
Instantiate(objectFromChunk, spawnPosition + spawnOrigin, Quaternion.identity);
platformslist.Add(objectFromChunk);
Debug.Log(TriggerExitN.positionN);
}
If I debug poisitionN here, it returns (0,0,0).
I know I'm missing something, I'd just like some help with it because it's driving me mad.
Please disregard the rest of the code that does not concern the value and usage of positionN for it is not yet well articulated.
Thank you in advance.
OnChunkExitedN();
positionN = gameObject.transform.position;
Just switch these two you are calling onchunkexitedn before setting positionN
positionN = gameObject.transform.position;
OnChunkExitedN();
I'm trying to make a fly-pan camera of a room when my first person player goes inside it. I've already recorded the animation on the camera but I'm having trouble when I change between cameras. I have read a lot a research about it but I actually don't know what I'm doing wrong. I would appreciate any kind of help. Thanks!
void OnTriggerEnter(Collider other)
{
inTrigger = true;
}
void OnTriggerExit(Collider other)
{
inTrigger = false;
}
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
// Update is called once per frame
void Update()
{
if (inTrigger/*&& !anim.isPlaying*/)
{
anim.Play("AnimateCameraFirst");
}
if (anim.IsPlaying("AnimateCameraFirst"))
{
cam.gameObject.SetActive(true);
FPCam.gameObject.SetActive(false);
}
else
{
FPCam.GetComponent<Camera>().enabled = true;
cam.GetComponent<Camera>().enabled = false;
}
}
Using Update() is not necessary here. Following code should be enough:
first, change FPCam GameObject to Camera to avoid GetComponent calls:
public Camera FPCam; // you have to reassign camera reference through inspector.
Camera cam; // as usual
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
void OnTriggerEnter(Collider other)
{
anim.Play("AnimateCameraFirst");
cam.enabled = true;
FPCam.enabled = false;
}
void OnTriggerExit(Collider other)
{
FPCam.enabled = true;
cam.enabled = false;
}
To Run some code when animation finishes you have to create animation event and register your method to it. Have a look at how to implement this.
you can create this method and register it to the event which fires right at the end of animation:
public void CameraFirstAnimation_Stopped()
{
FPCam.enabled = true;
cam.enabled = false;
}
Hope this helps.
I'm making a 2D Tower Defense game and want my towers to launch a prefab at minions. However it currently only spawns my desired prefab, but doesn't move it.
My two scripts:
public class Attacker : MonoBehaviour {
// Public variables
public GameObject ammoPrefab;
public float reloadTime;
public float projectileSpeed;
// Private variables
private Transform target;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider co){
if (co.gameObject.tag == "Enemy" || co.gameObject.tag == "BlockTower") {
Debug.Log("Enemy tag detected");
if(this.gameObject.tag == "Enemy" && co.gameObject.tag != "Enemy"){
Debug.Log("This is an Enemy");
// Insert for Enemey to attack Block Towers.
}
if(this.gameObject.tag == "Tower" && co.gameObject.tag != "BlockTower"){
Debug.Log("This is a Tower");
Tower Tower = GetComponent<Tower>();
Tower.CalculateCombatTime(reloadTime, projectileSpeed);
Transform SendThis = co.transform;
Tower.SetTarget(SendThis);
}
}
}
}
and
public class Tower : MonoBehaviour {
private Transform target;
private float fireSpeed;
private double nextFireTime;
private GameObject bullet;
private Attacker source;
// Use this for initialization
public virtual void Start () {
source = this.GetComponent<Attacker> ();
}
// Update is called once per frame
public virtual void Update () {
if (target) {
Debug.Log("I have a target");
//if(nextFireTime <= Time.deltaTime)
FireProjectile ();
}
}
public void CalculateCombatTime(float time, float speed){
Debug.Log("Calculate Combat Speed");
nextFireTime = Time.time + (time * .5);
fireSpeed = speed;
}
public void SetTarget(Transform position){
Debug.Log("Set Target");
target = position;
}
public void FireProjectile(){
Debug.Log("Shoot Projectile");
bullet = (GameObject)Instantiate (source.ammoPrefab, transform.position, source.ammoPrefab.transform.rotation);
float speed = fireSpeed * Time.deltaTime;
bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, speed);
}
}
Basicly Attacker detects the object that collides with it, then if its tag is Tower it will send the information to Tower. My debug shows that every function works, even "Debug.Log("Shoot Projectile");" shows up.
However it doesn't move towards my target so I guess "bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, step);" is never being executed?
Vector3.MoveTowards only moves the object once, it's just a instant displacement when the FireProjectile is called.
You need to create some kind of projectile script with an Update() function to make it move over time.
Here is an example:
public class Projectile : MonoBehaviour
{
public Vector3 TargetPosition;
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, TargetPosition, speed * Time.DeltaTime);
}
}
Then right after your bullet instantiation, set the target:
bullet.GetComponent<Projectile>().TargetPosition = target.position;
Hope it helps.
You have to update the position of the bullet. You are only moving when you create the bullet.
Try to make a list of bullets and use the update function to change the position.