i wrote a script for my 2D car game for an infinite track. So i got a "killer" behind my car, that destroys all ground objects coming out the screen on the left and on the right i have a spawner that spawns all my blocky sprites in it and it works pretty good.
Here is my update function:
void Update()
{
if(Mathf.Round(this.transform.position.x) == limit)
{
//Debug.Log("Limit reached");
limit += 10;
Spawn();
}
}
I attached the script to my camera and i have a start limit set to 160 (because i already got 15 10x10 sprites as start). Everytime my spawner passes the x-axis-limit it spawns a new sprite at this position and sets the next limit 10 higher. This works good if I don't drive too fast. Because then it stops spawning and the world is cut off. When I drive back to get my spawner back into the already spawned area and drive slowly again to it, it works again. I believe the method is just to inefficient to spawn fast enough so I maybe need another style of doing it.
For the understanding here the rest of the script:
using UnityEngine;
using System.Collections;
public class Spawner : MonoBehaviour
{
public GameObject[] obj;
private int limit = 160;
public int yAxisValue = -1;
public bool isOnlyDirt;
private ArrayList rotationValues = new ArrayList();
void Start()
{
//Spawn ();
int value = 0;
for(int i = 0; i < 4; i++)
{
rotationValues.Add(value);
//Debug.Log(rotationValues[i]);
value += 90;
}
}
void Spawn()
{
Vector3 pos = new Vector3 (Mathf.Round(transform.position.x), yAxisValue, transform.position.z);
Quaternion rotation = new Quaternion (0, 0, 0, 0);
if(isOnlyDirt)
{
int zRot = (int)rotationValues[Random.Range(0, rotationValues.Count - 1)];
//Debug.Log(zRot);
rotation = new Quaternion(0, 0, zRot, 0);
}
var SpawnedGround = Instantiate(obj[Random.Range(0, obj.GetLength(0))], pos, rotation);
(SpawnedGround as GameObject).gameObject.tag = "Gro
I also have a one-liner to move my spawner in front of my car so it can spawn things:
public float yCoord;
void Start ()
{
this.transform.position = new Vector3 (transform.position.x + 160, -yCoord, transform.position.z);
}
I parented it so i just have to write it in the start function.
Any ideas to improve this one?
Thanks
Sorry can't post as comments. You should try
if(Mathf.Round(this.transform.position.x) >= limit)
instead of
if(Mathf.Round(this.transform.position.x) == limit)
I think the floating point precision is skipping the checking.
Related
Following this question How to align sprites of smaller sizes to a moving gameobject sprite?, I'm trying to redraw my sprites to avoid rendering transparent pixels and try to maximize performance, and as I try starting this process with the sword sprites, I need to change the way the Sword game object follows the Player, which before was handled simply by using transform.position = hero.transform.position; because since both objects use sprites that are perfect squares they would have the same pivot point despite their size difference.
At first, it seemed to me the problem lied in how the pivots are different, thus I would need to update the transform position of the Sword every time its attack animation changes sprites. For that, I made a function which updates variables that influence the position as that gets updated:
here, heroTransform is a variable which gets sent the Player's transform property on the script's Start function as heroTransform = hero.transform;, where hero is defined right above it as hero = GameObject.Find("Hero");.
I would have expected this to make the Sword which is equipped with this script, called WieldablePosition to follow the player position, but it seems stuck in the same position as it starts:
I'm not sure, but I don't think I changed anything that would stop the Sword from moving. In what cases could a GameObject remain in a single place even as the transform.position is modified? For reference, please view the script:
using UnityEngine;
public class WieldablePosition : MonoBehaviour {
GameObject hero;
HeroMovement heroMovementScript;
private Transform heroTransform;
protected Animator anim;
private bool isAirAttackSingle;
private bool isFacingLeft;
private float x = 0;
private float y = 0;
void Start() {
hero = GameObject.Find("Hero");
heroTransform = hero.transform;
heroMovementScript = hero.GetComponent<HeroMovement>();
anim = GetComponent<Animator>();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
}
void Update() {
UpdatePosition();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
anim.SetBool("isAirAttackSingle", isAirAttackSingle);
if (isFacingLeft) {
transform.localScale = new Vector3(-1, 1, 1);
} else {
transform.localScale = Vector3.one;
}
}
public void SetPosition(AnimationEvent eventParams) {
string[] eventPositions = eventParams.stringParameter.Split(',');
x = float.Parse(eventPositions[0]);
y = float.Parse(eventPositions[1]);
}
private void UpdatePosition() {
transform.position = new Vector2(heroTransform.position.x + x, heroTransform.position.y + y);
}
}
I want my enemy to move back to starting position. He follows me until I get out of his range and then he just stops.
Also i want my skeleton to stop for like 5 sec, and then go back to starting point, any ideas ? I never did anything involving time, exept stopping it.
Here is my script for enemy:
Also here is a screenshoot of inspector on the skeleton: enemy
Here is my script for enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class chase : MonoBehaviour
{
public Transform player;
private Animator anim;
public float LookRadius = 15f;
public Transform enemyStartPos;
// Use this for initialization
void Start()
{
anim = GetComponent<Animator>();
this.enemyStartPos.position = this.transform.position;
}
// Update is called once per frame
void Update()
{
if (!PauseMenu.GameIsPaused)
{
if (Vector3.Distance(player.position, this.transform.position) < 15)
{
Vector3 direction = player.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 3)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
else
{
anim.SetBool("isAttacking", true);
anim.SetBool("isWalking", false);
}
}
else
{
if (Vector3.Distance(this.enemyStartPos.position, this.transform.position) >1)
{
Vector3 direction = this.enemyStartPos.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 1)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
}
else
anim.SetBool("isIdle", true);
anim.SetBool("isAttacking", false);
anim.SetBool("isWalking", false);
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, LookRadius);
}
}
Before you try to "code" this, think about it in terms of your program design. You have designed and implemented behavior that says "if the bot's position is within X units of the player's position, turn the bot and move it toward the player."
What you describe wanting to do next can be thought of in the same way (which should lead to similar code).. "else, if the bot's position is NOT within X units of the player's position, turn the bot and move it toward [origin]." Note, this means you need to define what [origin] is for the bot. Point being, it makes no difference in the code whether you are moving toward a player or some arbitrary fixed point. The code to move one transform toward another transform is the same.
For "wandering" it's essentially the same thought process: If bot is within X units of [origin] and not following player, pick a random direction and move that way. (better yet, pick a random direction and move that way for some amount of time so your bot doesn't just jitter around origin).
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.
My Raycastrotates around the player and scans the 2D-Area. It should recognize another Gameobject, which is tagged as Pick Up, but found is always false.
using UnityEngine;
using System.Collections;
public class Findway : MonoBehaviour {
public GameObject Player;
// Use this for initialization
void Start () {
transform.position = Player.transform.position;
}
RaycastHit hit;
float[] distance = new float[360];
int moveAngle = 0;
bool found = false;
// Update is called once per frame
void Update () {
transform.position = Player.transform.position;
for (int iii = 0; iii < 360; iii++)
{
Vector3 rayway = new Vector3 (Mathf.Cos(180 * iii / Mathf.PI), 0, Mathf.Sin(180 * iii / Mathf.PI));
if (Physics.Raycast (transform.position, rayway, out hit, 100f))
{
distance [iii] = hit.distance;
//Debug.Log(hit.collider.gameObject);
//Debug.DrawRay(transform.position, transform.forward, Color.green);
if (hit.collider.CompareTag ("Pick Up"))
{
moveAngle = iii;
found = true;
}
}
}
}
}
Use your ray cast out of the for loop , a good cpu will not take long to run the iteration till 360 and using it in update will cause it to run infinite times causing more problems, always use divide and conquer , minimize the code , remove everything and just run the ray to check if its working without any rotation , then apply rotation ,do tell if it worked .Double Check if there is a object tagged as "Pick Up" . Also never use a for loop in update without an explicit condition to stop it like a bool otherwise it keeps on running .
Basically this game is a brick breaker in which I will be giving a quick basic background about it. The game will spawn 10 bricks at random locations, and if the all hits the brick
once, the brick is destroyed. If all 10 bricks are destroyed then the user will be taken to the other scene (gamewon).
My main problem is that when I destroy all the bricks, I am not being redirected to the scene. However if I set the numberOfBricks to 1, it will work.. I can't really understand what's happening.
Thanks alot guys!
-HurpaDurpa
using UnityEngine;
using System.Collections;
public class BrickScript : MonoBehaviour {
public int position_x = 0;
public int position_y = 0;
public GameObject Brick;
public int brickDamage = 0;
public int numberOfBricks = 10;
// Use this for initialization
void Start () {
Brick = GameObject.Find ("Brick");
position_x = Random.Range (-6, 6);
position_y = Random.Range(-1, 4);
transform.position = new Vector3 (position_x, position_y, transform.position.z);
}
// Update is called once per frame
void Update () {
if (brickDamage == 1) { //destroy brick on 1 hit
numberOfBricks--;
Object.Destroy (gameObject); //so it will destroy the hit object
}
if (numberOfBricks == 0) {
Application.LoadLevel (1);
numberOfBricks = 10;//Variable resetted
}
}
void OnCollisionEnter(Collision collision)
{
brickDamage++;
}
}
This is happening because you seem to have attached the script above to each brick.
So what is really happening is when any one of your bricks get hit, the numberOfBricks reduces by 1, resulting the value going to 9. This will happen to each brick.
Rather, what you need to do is to keep this numberOfBricks counter in another script. In the OnCollisionEnter portion of your code, you should reduce the numberOfBricks variable in the other script by 1.
Do that and you'll see the result you expect.