I have a spawner game object the spawns 2 falling prefabs randomly.
The first version of the spawner only contained the spawning system and the falling effect was a component that the prefabs had, the problem was that I wanted that as the game progresses the SpawnRatio and falling speed will get higher with synchronization so I merged the spawning and the falling systems into the spawner game object and now for some reason the objects are spawning but no falling down.
public class Random_Spawn : MonoBehaviour {
public GameObject cube, circle;
public float spawnRate = 2f;
static float fallSpeed = 2f;
float nextSpawn = 0f;
int whatToSpawn;
void Update()
{
if (Time.time > nextSpawn)
{
whatToSpawn = Random.Range(1, 3);
switch (whatToSpawn)
{
case 1:
GameObject myCube = (GameObject)Instantiate(cube, transform.position, Quaternion.identity);
myCube.transform.Translate(Vector2.down * fallSpeed * Time.deltaTime, Space.World);
myCube.transform.parent = transform;
break;
case 2:
GameObject myCircle = (GameObject)Instantiate(circle, transform.position, Quaternion.identity);
myCircle.transform.Translate(Vector2.down * fallSpeed * Time.deltaTime, Space.World);
myCircle.transform.parent = transform;
break;
}
nextSpawn = Time.time + spawnRate;
if (spawnRate >= 0.05)
{
spawnRate -= 0.04f;
fallSpeed -= 0.04f;
}
}
}
}
The problem is that you are only calling Vector.Translate once for each spawned object. You need this to be called every frame for the movement to continue.
It would be best to attach a separate movement script to the cube and circle prefabs that calls Vector.Translate in their own Update() functions.
To handle the increasing fall speed you can have the new movement script reference a fallSpeed variable from your spawner script to adjust your Vector.Translate speed.
speed = spawner.GetComponent<Random_Spawn>().fallSpeed;
Related
I have a SMALL a problem. In Unity 3D 2020 Beta, I've put a player with a sphere collider on it and some cubes (walls) with box colliders. I've added a player controller script to the player object.
I've put the camera above the plane where the player and the walls are on, and I've made that the player should rotate to face the mouse position. I used rigidbody.AddForce for movement in a FixedUpdate function.
The player controller script is attached below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Header("Keys")]
public KeyCode forward;
public KeyCode backward;
public KeyCode left;
public KeyCode right;
public KeyCode fire;
[Header("Health")]
public int hitpoints = 3;
[Header("Movement")]
public float speed;
public float turningSpeed;
[Header("Shooting")]
public GameObject bulletPrefab;
public Transform bulletSpawner;
public float bulletSpeed;
public float reloadTime;
private float currentReload;
private Rigidbody rb;
private Quaternion targetRotation;
void Start()
{
rb = GetComponent<Rigidbody>();
currentReload = reloadTime;
}
void LateUpdate()
{
if (hitpoints == 0)
Die();
// Rotation
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
targetRotation = Quaternion.LookRotation(hit.point - transform.position);
Debug.DrawLine(transform.position, hit.point, Color.white);
}
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turningSpeed * Time.deltaTime);
transform.eulerAngles = new Vector3(0, transform.rotation.eulerAngles.y, 0);
currentReload += Time.deltaTime;
// Shooting
if (Input.GetKeyDown(fire) && currentReload >= reloadTime)
{
currentReload = 0f;
GameObject bulletGO = Instantiate(bulletPrefab, bulletSpawner.position, transform.rotation);
bulletGO.transform.position = bulletSpawner.position;
Bullet bulletScript = bulletGO.GetComponent<Bullet>();
bulletScript.speed = bulletSpeed;
Destroy(bulletGO, 5f);
}
}
void FixedUpdate()
{
// Movement
if (Input.GetKey(forward))
{
rb.AddForce(Vector3.forward * speed, ForceMode.Force);
}
if (Input.GetKey(backward))
{
rb.AddForce(-Vector3.forward * speed, ForceMode.Force);
}
if (Input.GetKey(left))
{
rb.AddForce(Vector3.left * speed, ForceMode.Force);
}
if (Input.GetKey(right))
{
rb.AddForce(Vector3.right * speed, ForceMode.Force);
}
//transform.position = new Vector3(transform.position.x, 10, transform.position.z);
// ON RIGIDBODY I HAVE CONSTRAINS:
// POSITION: Y (thats why I commented the line above)
// ROTATION: X, Z (topdown -> so I want only rotation on Y)
}
private void Die()
{
Destroy(gameObject);
}
}
But the problem is when the player hits very hard a wall, the sphere collider starts shaking and the player does not look at the mouse position exactly (it is somewhere 10 degrees away most of the times - it depends on how hard do I hit the walls).
I can record if it helps. If you want any information, feel free to ask! Any help will be appreciated! :)
The problem here seems to be you're directly altering the transform of your object despite having a Rigidbody component. Generally you should avoid altering a transform directly when you have a Rigidbody attached, especially a non-kinematic one, as by attaching one you are signalling that the object is to be controlled by the physics simulation.
Solutions I would explore:
If you don't need a rigidbody, don't use one
If you can avoid altering the transform directly, then do not do so. You can rotate objects by applying torque and the likes
Try setting your object to kinematic if you don't need collisions to affect the rigidbody's physics
Manually set the torque and velocity of your object to 0 each fixed update
I'm making an isometric 3D game. I made two joystick, one to move the player and the other one to shoot a projectile when the joystick is released. I made 3 attemps to achieve this result,but there's every time a problem. first try was with this :
clone.velocity = transform.TransformDirection(newpos);
but this need a rigidbody and a projectile can't be a rigidbody because it start from inside of the player.
Second try was this:
clone.transform.Translate(dir * (launchForce));
but this doesn't have a "speed" so it just move instantly to the position, not by moving, but translating
and the same happens with the third attemp:
clone.transform.position=Vector3.MoveTowards(Player.transform.position,newpos,10f);
This is the best solution until now because it gives me the possibility to choose a max range between the start posititon and the newposition.
Here's the full code:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class shoot : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
private Vector3 inputVector;
public GameObject proiettile;
private Vector3 dir = Vector3.zero;
private Vector3 newpos;
public float launchForce;
public Rigidbody Player;
private GameObject clone;
private void Start()
{
bgImg = GetComponent<Image>();
joystickImg = transform.GetChild(0).GetComponent<Image>();
}
public virtual void OnDrag(PointerEventData ped)
{
Vector2 pos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(bgImg.rectTransform, ped.position, ped.pressEventCamera, out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
inputVector = new Vector3(pos.x * 2 +1, 0, pos.y * 2 - 1);
inputVector = (inputVector.magnitude > 1.0f) ? inputVector.normalized : inputVector;
// Move joystickImg
joystickImg.rectTransform.anchoredPosition =
new Vector3(inputVector.x * bgImg.rectTransform.sizeDelta.x / 3
, inputVector.z * (bgImg.rectTransform.sizeDelta.y / 3));
}
}
public virtual void OnPointerDown(PointerEventData ped)
{
OnDrag(ped);
}
public virtual void OnPointerUp(PointerEventData ped)
{
dir.x = Horizontal();
dir.z = Vertical();
newpos = dir * (launchForce);
clone = Instantiate(proiettile, Player.transform.position, Player.transform.rotation);
//third attempt
//clone.transform.position=Vector3.MoveTowards(Player.transform.position,newpos,10f);
//second attempt
//clone.transform.Translate(dir * (launchForce));
//first attempt
//clone.velocity = transform.TransformDirection(newpos);
// joystick come back to start position
inputVector = Vector3.zero;
joystickImg.rectTransform.anchoredPosition = Vector3.zero;
//temporary solution to replace the absence of a max range for projectile
clone.timeoutDestructor = 5;
}
public float Horizontal()
{
if (inputVector.x != 0)
return inputVector.x;
else
return Input.GetAxis("Horizontal");
}
public float Vertical()
{
if (inputVector.z != 0)
return inputVector.z;
else
return Input.GetAxis("Vertical");
}
}
If I were you I would use rigidbody attempt. Just make sure it does not collide with player by setting collision layers/disabling collider for first fraction of second/spawning projectile outside of player.
Why?
Two main reasons:
Rigidbody will handle interpolation for you out of the box (without it movement may be glitchy)
Rigidbody will handle "between frame" collisions with CCD for you out of the box (without it your projectile may go trough walls, or even targets if it's fast enough)
And these two features will save you a lot of time later
One of attempts:
When I spawn projectile I check with SphereCast (or whatever shape it has) if it's colliding with something. If it is I change isTrigger to true and then in "OnTriggerExit" I change isTrigger to false again. If it is not colliding with anything on spawn I just set isTrigger to false at start and that should do the trick.
Keeping my Photon project under given 500 msg/s is really tricky. Even with 10 players in room each updating position 10 times per second 10(players) * 10(sent msg) * 10(received msg) = 1000 msg/s is generated. That is just the player movement. Next I need to move bullets around which will increase the amount of messages once more.
At the moment I have bullets instantiated across network but only local player is able to move it since I don't yet sync movement of bullets. I was wondering could I have all of the clients start moving the bullets on their local device once it's instantiated instead of passing position through network? This would save a lot of messages since I would never have to send bullet positions over network.
Hacking and cheating is not a problem in my game.
EDIT: this is the script I'm using at the moment to move bullets. This only works locally on the device that bullet was instantiated on. How would I run this script locally on each client?
public class Network_Bullet : Photon.MonoBehaviour {
private Rigidbody2D rb;
[HideInInspector]
public float speed = 0;
[HideInInspector]
public Vector2 direction = Vector2.zero;
public void SetValues(float _speed, Vector2 _direction)
{
rb = GetComponent<Rigidbody2D> ();
this.speed = _speed + 150f; // bullet has 150 more speed than player
this.direction = _direction;
}
private void Update()
{
if (speed != 0)
{
rb.velocity = direction * speed * Time.fixedDeltaTime;
}
}
}
And here is how to bullet is instantiated:
private void OnClick_Shoot()
{
if (photonView.isMine == true)
{
if (timeSinceLastBullet >= spawnTime)
{
GameObject newBullet = PhotonNetwork.Instantiate (Path.Combine ("prefabs", "Network Bullet"), transform.position, transform.rotation, 0);
newBullet.GetComponent<Network_Bullet> ().SetValues (owner.speed, new Vector2(owner.last_horizontal, owner.last_vertical));
timeSinceLastBullet = 0f;
}
else
{
Debug.Log ("loading...");
}
}
}
First, 10 players with 10 movement messages per second generates 100 messages per second per room.
To the actual question - for bullets, all you need to do is instantiate and give it initial rotation and speed. Because bullets usually don't change their rotation or speed (at least in video games), you only need to do that and only update the bullets in everyone's local machines.
You can try this:
private void OnClick_Shoot()
{
if (timeSinceLastBullet >= spawnTime)
{
int level = 2; //it is not need for you, but you can setup some params on object
// for example i set level for increase damage per level.
string someMsg = "hi i'm bullet";
object[] parametres = new object[] {
level,
someMsg
}
PhotonNetwork.Instantiate (Path.Combine ("prefabs", "Network Bullet"), transform.position, transform.rotation, 0, parametres );
timeSinceLastBullet = 0f;
}
else
{
Debug.Log ("loading...");
}
}
And bullet, just add PhotonView component on your prefab and it will be work
public class Bullet : MonoBehaviour {
private Rigidbody2D rb;
[HideInInspector]
public float speed = 0;
[HideInInspector]
public Vector3 direction = Vector3.zero;
void Start()
{
rb = GetComponent<Rigidbody2D> ();
this.speed = _speed + 150f; // bullet has 150 more speed than player
this.direction = _direction;
PhotonView pView = gameObject.GetPhotonView();
// or
//PhotonView pView = gameObject.GetComponent<PhotonView>();
damage = 10 * pView[0]; // increase damage per level
Debug.Log(pView[1]);
}
void Update()
{
if (speed != 0)
{
rb.velocity = direction * speed * Time.fixedDeltaTime;
}
}
}
So, that methods will be work localy on all clients. I'm using that on my bullets.
So i'm trying to change weapons for my 2d top down space shooter. by weapon I just mean my bullet prefab so it shoots a different bullet which I can then add damage to etc.
here is the code I have. when I press number 2 it shoots my prefab clone in the hierarchy but its greyed out and nothing shows up in the game view. Below is my playerShoot code.
public class playerShoot : MonoBehaviour {
public Vector3 bulletOffset = new Vector3 (0, 0.5f, 0);
float cooldownTimer = 0;
public float fireDelay = 0.25f;
public GameObject bulletPrefab;
int bulletLayer;
public int currentWeapon;
public Transform[] weapons;
void Start () {
}
void Update () {
if (Input.GetKeyDown(KeyCode.Alpha1)){
ChangeWeapon(0);
}
if (Input.GetKeyDown(KeyCode.Alpha2)){
ChangeWeapon(1);
}
cooldownTimer -= Time.deltaTime;
if (Input.GetButton("Fire1") && cooldownTimer <= 0){
cooldownTimer = fireDelay;
Vector3 offset = transform.rotation * bulletOffset;
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
bulletGO.layer =gameObject.layer;
}
}
public void ChangeWeapon(int num){
currentWeapon = num;
for (int i = 0; i < weapons.Length; i++){
if (i ==num)
weapons[i].gameObject.SetActive(true);
else
weapons[i].gameObject.SetActive(false);
}
}
}
Keeping the rest of the code same, just change the following line
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
to
GameObject bulletGO = (GameObject)Instantiate(weapons[currentWeapon].gameObject, transform.position + offset, transform.rotation);
What the change does is use the transforms for the weapons in your weapon array, rather than using the bulletPrefab.
The problem occured because you were Instantiating a single prefab, which was the same as the Transform you used for the first element in your weapons array. So, when you call ChangeWeapon(1), the prefab would get deactivated. This reulted in inactive GameObjects being instantiated.
What I would suggest you do is to have two separate prefabs and spawn those accordingly.
I have a bullet that has to hit a constantly moving enemy.
So, in BulletScript, I have declared a Transform
public Transform enemy; //and assigned enemy object to it that is continuously moving and changing its position
Now, when I try to use enemy.position in bullet script so as to hit it, enemy.positiongives the position at which the enemy strted and not the position at which it was when bulletprefab was shot.
How can I get the updated position of enemy object every time bulletprefab is instantiated.
This is how I am changing the enemy's position:
void Update () {
float amttomove = currentSpeed * Time.deltaTime;
transform.Translate (Vector3.left * amttomove);
if (transform.position.x < 0f)
setposandspeed();
}
void setposandspeed()
{
x = 11.5f;
z = 0.0f;
currentSpeed = Random.Range (MinSpeed, MaxSpeed);
y = Random.Range (0f, 2.5f);
transform.position = new Vector3(x, y, z);
}
This is where tried to use enemy's position in bulletscript:
float target_Distance = Vector3.Distance(Projectile.position, Target.transform.position );
It is called inside Start() of bulletscript
This is where I instantiated the bullet in Player class:
Inside Updated method:
if (Input.GetKeyDown ("space")) {
Vector3 position = new Vector3 (transform.position.x, transform.position.y + collider.bounds.size.y / 2);
Instantiate (bulletprefab, position, Quaternion.identity);
}
enemy.position in bullet script so as to hit it, enemy.positiongives
the position at which the enemy strted and not the position at which
it was when bulletprefab was shot.
No, enemy.position will return the current position of the enemy, suitable if you want your projectile follow the enemy.
If you are instantiating dynamically your projectiles (BullerScript) and want to shoot them toward the position the enemy is during the shoot frame, record it just after bullet is instantiated for example:
class BulletScript : MonoBehavior
{
public Vector3 targetPos;
void Update()
{
//move toward targetPos
}
}
BulletScript bullet = GameObject.Instantiate(bulletPrefab,shootPosition) as BulletScript;
bullet.targetPos = enemyPosition;