Setting: Creating my first multiplayer game and running into an odd issue.
it's a tank game where players can shoot bullets and kill each other.
You can charge the bullets to shoot it faster/further away.
Problem:
When the client player charges fully and releases, the bullet continue to be spawned repeatedly and never stops. This issue doesn't occur if the client player does Not charges fully.
I believe the issue is the update function within the if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired)
Note:
The host player does not have this problem, therefore it's somehow related to networking.
private void Update()
{
if (!isLocalPlayer)
return;
// Track the current state of the fire button and make decisions based on the current launch force.
m_AimSlider.value = m_MinLaunchForce;
if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired) {
m_CurrentLaunchForce = m_MaxLaunchForce;
CmdFire ();
} else if (Input.GetButtonDown (m_FireButton) && !m_Fired) {
m_Fired = false;
m_CurrentLaunchForce = m_MinLaunchForce;
m_ShootingAudio.clip = m_ChargingClip;
m_ShootingAudio.Play();
} else if (Input.GetButton (m_FireButton)) {
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;
m_AimSlider.value = m_CurrentLaunchForce;
} else if (Input.GetButtonUp(m_FireButton)) {
CmdFire ();
}
}
[Command]
private void CmdFire()
{
// Set the fired flag so only Fire is only called once.
m_Fired = true;
// Create an instance of the shell and store a reference to it's rigidbody.
GameObject shellInstance = (GameObject)
Instantiate (m_Shell, m_FireTransform.position, m_FireTransform.rotation) ;
// Set the shell's velocity to the launch force in the fire position's forward direction.
shellInstance.GetComponent<Rigidbody>().velocity = m_CurrentLaunchForce * m_FireTransform.forward;
// Change the clip to the firing clip and play it.
m_ShootingAudio.clip = m_FireClip;
m_ShootingAudio.Play ();
NetworkServer.Spawn (shellInstance);
// Reset the launch force. This is a precaution in case of missing button events.
m_CurrentLaunchForce = m_MinLaunchForce;
}
If you look trough the documentation you will find this -> [Command] functions are invoked on the player object associated with a connection. This is setup in response to the "ready" message, by passing the player objec to the NetworkServer.PlayerIsReady() function. The arguments to the command call are seriialized across the network, so that the server function is invoked with the same values as the function on the client.
I think the reason why it wasnt working for you is because you have to pass an argument to the function like
[Command]
private void CmdFire(bool m_Fired)
{
// Set the fired flag so only Fire is only called once.
m_Fired = true;
// Create an instance of the shell and store a reference to it's rigidbody.
GameObject shellInstance = (GameObject)
Instantiate (m_Shell, m_FireTransform.position, m_FireTransform.rotation) ;
// Set the shell's velocity to the launch force in the fire position's forward direction.
shellInstance.GetComponent<Rigidbody>().velocity = m_CurrentLaunchForce * m_FireTransform.forward;
// Change the clip to the firing clip and play it.
m_ShootingAudio.clip = m_FireClip;
m_ShootingAudio.Play ();
NetworkServer.Spawn (shellInstance);
// Reset the launch force. This is a precaution in case of missing button events.
m_CurrentLaunchForce = m_MinLaunchForce;
}
And then call it like this:
CmdFire(m_Fired) where m_Fired has to point to the players own variable.
Related
I'm just starting out please excuse vast ignorance.
I'm writing a c# script in unity as part of the essentials training. I'm doing the 3d audio module and I thought I'd try and get a little bit fancier than the scope of this particular lesson which is supposed to be having an object fly through a window in a pre-built scene and make a 3d sound as it moves.
I wanted to make the movement of the object conditional upon a player moving close to it in 3d space. I figured out how to trigger the movement of an object in a script with an if statement that changes the transform parameters of the object the script is attached to when a 'distanceFromObject' variable is < 2. It works, however the script runs in the update section of the script which runs once every frame. This means that the object's transform parameters are changed every frame as expected but of course stops doing so when the distance between the object that's moving and the player exceeds 2.
I see the mistake I've made because if the object moves away when the player gets close then it will inevitably eventually move far enough away that the distanceFromObject variable will grow bigger than 2 whereupon it stops and just hovers in place. I don't know how to fix it though.
I need the script to check the distance between the object and the player every frame so that it will trigger the instance the player gets close enough, and when they get close enough, I need the object to move away, however once it has been triggered to move, I need the object to continue moving, but the script to stop checking what the distance is anymore.
The script looks like this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyOff : MonoBehaviour
{
public Vector3 rotateChange;
public Vector3 positionChange;
public float distanceFromObject;
public GameObject character;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
Use flags instead of writing your logic in the if statement :
public class FlyOff : MonoBehaviour
{
// fields removed for more readability
// use a flag that's set to true/false
private bool isCloseEnough = false;
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
// set the flag to true when player is close enough
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
// even if the player gets far, the flag will remain true
if (isCloseEnough)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
You can even apply the opposite logic to stop the object to move away when it has reach a certain distance :
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
else if (distanceFromObject > SomeValue)
{
isCloseEnough = false;
}
If I understand correctly you could just add a bool flag and set it once you are close enough. Then you can start moving and skip further distance checks but keep moving forever.
private bool flyAway;
void Update()
{
if(!flyAway)
{
distanceFromObject = Vector3.Distance(character.transform.position, transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
flyAway = true;
}
}
else
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
In general: Avoid using print every frame! Even if you user doesn't see the log in a built app it is still causing overhead!
This is the script taken from the tanks asset store and I want to convert the shooting which is currently being done by space bar to Ui Shoot button. It should fire the bullet.
Like I want that when my button is pressed it should charge as to how much velocity is imparted to the bullet and it soon as I release it the bullet should be fired.
Also, I was thinking of a fill image which can show that thing in the button as well.
public class TankShooting: MonoBehaviour
{
//public int m_PlayerNumber = 1; // Used to identify the different players.
public Rigidbody m_Shell; // Prefab of the shell.
public Transform m_FireTransform; // A child of the tank where the shells are spawned.
public Slider m_AimSlider; // A child of the tank that displays the current launch force.
//public AudioSource m_ShootingAudio; // Reference to the audio source used to play the shooting audio. NB: different from the movement audio source.
//public AudioClip m_ChargingClip; // Audio that plays when each shot is charging up.
//public AudioClip m_FireClip; // Audio that plays when each shot is fired.
public float m_MinLaunchForce = 15f; // The force is given to the shell if the fire button is not held.
public float m_MaxLaunchForce = 30f; // The force is given to the shell if the fire button is held for the max charge time.
public float m_MaxChargeTime = 0.75f; // How long the shell can charge for before it is fired at max force.
private string m_FireButton; // The input axis that is used for launching shells.
private float m_CurrentLaunchForce; // The force that will be given to the shell when the fire button is released.
private float m_ChargeSpeed; // How fast the launch force increases, based on the max charge time.
private bool m_Fired; // Whether or not the shell has been launched with this button press.
private float nextFireTime;
private void OnEnable()
{
// When the tank is turned on, reset the launch force and the UI
m_CurrentLaunchForce = m_MinLaunchForce;
m_AimSlider.value = m_MinLaunchForce;
}
private void Start ()
{
// The fire axis is based on the player number.
m_FireButton = "Fire1"; //+ m_PlayerNumber;
// The rate that the launch force charges up is the range of possible forces by the max charge time.
m_ChargeSpeed = (m_MaxLaunchForce - m_MinLaunchForce) / m_MaxChargeTime;
}
public void Update()
{
// The slider should have a default value of the minimum launch force.
m_AimSlider.value = m_MinLaunchForce;
// If the max force has been exceeded and the shell hasn't yet been launched...
if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired)
{
// ... use the max force and launch the shell.
m_CurrentLaunchForce = m_MaxLaunchForce;
Fire (m_CurrentLaunchForce, 1);
}
// Otherwise, if the fire button has just started being pressed...
else if (Input.GetButtonDown (m_FireButton))
{
// ... reset the fired flag and reset the launch force.
m_Fired = false;
m_CurrentLaunchForce = m_MinLaunchForce;
// Change the clip to the charging clip and start it playing.
//m_ShootingAudio.clip = m_ChargingClip;
//m_ShootingAudio.Play ();
}
// Otherwise, if the fire button is being held and the shell hasn't been launched yet...
else if (Input.GetButton (m_FireButton) && !m_Fired)
{
// Increment the launch force and update the slider.
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;
m_AimSlider.value = m_CurrentLaunchForce;
}
// Otherwise, if the fire button is released and the shell hasn't been launched yet...
else if (Input.GetButtonUp (m_FireButton) && !m_Fired)
{
// ... launch the shell.
Fire (m_CurrentLaunchForce, 1);
}
}
public void Fire (float launchForce, float fireRate)
{
if (Time.time > nextFireTime)
{
nextFireTime = Time.time + fireRate;
// Set the fired flag so only Fire is only called once.
m_Fired = true;
// Create an instance of the shell and store a reference to it's rigidbody.
Rigidbody shellInstance =
Instantiate (m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;
// Set the shell's velocity to the launch force in the fire position's forward direction.
shellInstance.velocity = m_CurrentLaunchForce * m_FireTransform.forward;
// Change the clip to the firing clip and play it.
//m_ShootingAudio.clip = m_FireClip;
//m_ShootingAudio.Play ();
// Reset the launch force. This is a precaution in case of missing button events.
m_CurrentLaunchForce = m_MinLaunchForce;
}
}
}
There is Very Easy Solution for this:
Create A Function In Your Script
ShootTheBullet()
and The Create A UI Button and Click on Add Button In your Button's Component Event
"On Click"
Drag and Drop The gameObject that you have attached your C# script From Hierarchey that Contains "ShootTheBullet()" and then click On Right Option Select your Script an Select That Function.
Now you are ready to Shoot Bullets With UI Button.
My script is about when my ball hit a "Trap Object", it'll be moved to start position and STOP right there. How to do that?
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag ( "Trap" ))
{
//move object to start position
transform.position = startposition.transform.position;
// I want to stop the object here, after it was moved to start position. Because my ball was moving when it hit Trap object, so when it was moved to start position, it keeps rolling.
}
}
As I mentioned in my comment, you need to reset the force of the rigidbody to make sure that your ball is stopped completely. The following code could fix your issue.
// LateUpdate is triggered after every other update is done, so this is
// perfect place to add update logic that needs to "override" anything
void LateUpdate() {
if(hasStopped) {
hasStopped=false;
var rigidbody = this.GetComponent<Rigidbody>();
if(rigidbody) {
rigidbody.isKinematic = true;
}
}
}
bool hasStopped;
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag ( "Trap" ))
{
var rigidbody = this.GetComponent<Rigidbody>();
if(rigidbody) {
// Setting isKinematic to False will ensure that this object
// will not be affected by any force from the Update() function
// In case the update function runs after this one xD
rigidbody.isKinematic = false;
// Reset the velocity
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
hasStopped = true;
}
//move object to start position
transform.position = startposition.transform.position;
// I want to stop the object here, after it was moved to start position. Because my ball was moving when it hit Trap object, so when it was moved to start position, it keeps rolling.
}
}
The code is untested, so I wouldnt be suprised if it didnt compile on first try, I could have misspelled Rigidbody or something.
(I don't have Unity at work either so hard to test ;-))
Hope it helps!
Do you add some form of speed or velocity to your ball? If you do, you need to reset this to zero to stop your ball from rolling.
I am looking for a code or script in C# or Java to make my cube tagged as a Player jump, in below script.
I have written some code and attached it to a button on canvas, but the problem is when I press and hold the button, it keeps jumping and makes an infinitly high jump.
Here is my script written in C#
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class DownstateButton : Button
{
public void Update()
{
//A public function in the selectable class which button inherits from.
if(IsPressed())
{
WhilePressed();
}
}
public void WhilePressed()
{
var player =GameObject.FindWithTag("Player");
player.transform.Translate(0, 1, 0);
//It was for jump
}
}
Hook the PointerDown (called when the button is pressed down) and PointerUp (button has been let go again) events from the UIButton and weight the translation of the position with Time.deltaTime, and you should be good to go. (player.transform.Translate(0,1 * Time.deltaTime, 0), optionally multiply it with another factor for speed modulation.) References: http://unity3d.com/learn/tutorials/modules/beginner/ui/ui-events-and-event-triggers
EDIT: Yeah, some example code. First, I have an EventTrigger component on the button. I use this sothat I can hook the PointerDown and PointerUp events as described before. It looks like this in the inspector:
(Use the "Add New Event Type" button to redirect the event calls.)
Then, I have this script on the button. The code speaks for itself.
using UnityEngine;
using UnityEngine.EventSystems;
public class JumpButton : MonoBehaviour {
private bool shouldJump = false;
// Update is called once per frame
void Update () {
//Find the player
var player = GameObject.FindGameObjectWithTag("Player");
//No player? exit out.
if (player == null)
return;
//Is the jump button currently being pressed?
if (shouldJump)
{
//Translate it upwards with time.
player.transform.Translate(new Vector3(0, Time.deltaTime * 5, 0));
//Make sure the Rigidbody is kinematic, or gravity will pull us down again
if (player.GetComponent<Rigidbody>().isKinematic == false)
player.GetComponent<Rigidbody>().isKinematic = true;
}
//Not jumping anymore? reset the Rigidbody.
else
player.GetComponent<Rigidbody>().isKinematic = false;
}
//When the button is being pressed down, this function is called.
public void ButtonPressedDown(BaseEventData e)
{
shouldJump = true;
}
//When the button is released again, this function is called.
public void ButtonPressedUp(BaseEventData e)
{
shouldJump = false;
}
}
The thing with switching to a kinematic rigidbody is optional, though. Also the speed can be adjusted with the multiplicative constant in the Translate() call. I tested this code with a standard cube, that has the Player tag and a Rigidbody on it, and it works fine.
Happy coding.
You could use a unity coroutine.
At the start of the routine, you'd set (for example) "isJumping" (a bool), and then before you start your loop bit, you'd check if you are jumping by checking 'isJumping'.
If not "isJumping", set isJumping to true, do your jump, and then on the completion of the routine, set isJumping to false.
//untested (uncompiled) code written on the fly
bool isJumping = false;
IEnumerator doJump()
{
if (!isJumping) {
isJumping = true;
// do jump (probably a loop)
while (jumpCondition) {
// jump instructions
yield return
}
//unset isJumping, inside 'if' but after yield return
isJumping = false
}
}
Note : code after yield return in a coroutine will only (probably) be run once, and only run as the coroutine exists (because no more yielding means the coroutine is at an end)
I'm trying to create a simple platformer. I have triggers set around where if the player collides, ConstantForce is enabled on the player, and the player is pushed in a chosen direction (depending on the trigger). This works relatively well with pushing the player left and right, but where I'm having an issue is when the player is pushed up on the Y axis. The player hovers for a bit but each time he drops, he begins to go lower and lower- eventually exiting the trigger. I want him to maintain a certain height in air unless he Exits the trigger. Increasing ConstantForce only makes him fly higher, decreasing his mass has a similar effect.
private ConstantForce getConstantForce;
public GameObject findingThePlayer;
public Vector3 forceDirection = new Vector3();
void Start()
{
//Getting the ConstantForce component from player
findingThePlayer = GameObject.Find("pig_test");
getConstantForce = findingThePlayer.GetComponent<ConstantForce>();
}
void OnTriggerStay()
{
//turning on the players ConstantForce
getConstantForce.enabled = true;
//we set the vector value in editor, depending which way we want the wind to blow
getConstantForce.constantForce.force = forceDirection;
}
void OnTriggerExit()
{
//When player exits trigger, ConstantForce is disabled
getConstantForce.enabled = false;
}
}
Hacky way
void OnTriggerStay()
{
//turning on the players ConstantForce
getConstantForce.enabled = true;
//we set the vector value in editor, depending which way we want the wind to blow
getConstantForce.constantForce.force = forceDirection;
//Once you reach the height you want, set the body to be kinematic,
// that way forces will not longer have any effect
findingThePlayer.rigidbody.isKinematic = true;
}
void OnTriggerExit()
{
//When player exits trigger, ConstantForce is disabled
getConstantForce.enabled = false;
//And lets make it a physics rigidbody again on exit
findingThePlayer.rigidbody.isKinematic = false;
}
Another Method is to play with the gravity values until it feels right. You can do this by going into Edit -> Project Settings -> Physics