Im working on a project that I want to create a power up effect whenever the button "Q" is pressed, I have the animation working and the character, I also have the spawning objects around my player that I want to spawn (See Figure below)
My question is how to add different gravity on each rock (spawning object).
Here is the script that I'm currently using.
/* Public Variables Declaration */
public Transform spawn_LocationForSmall;
public Transform spawn_LocationForMedium;
public Transform spawn_LocationForLarge;
public GameObject smallRock_Prefab;
public GameObject mediumRock_Prefab;
public GameObject largeRock_Prefab;
/* Private Variables Declaration */
private GameObject[] smallRocks_List;
private float posX, posY, posZ;
private bool smallCount = false;
private bool mediumCount = false;
private bool largeCount = false;
private bool small_CheckPos = false;
private bool medium_CheckPos = false;
private bool large_CheckPos = false;
void Start() {
//smallRocks_List = GameObject.FindGameObjectsWithTag("smallRock");
Create_Small_Rocks();
Create_Medium_Rocks();
Create_Large_Rocks();
}
private void Create_Small_Rocks(){
for(int i=0; i<=20; i++){
small_CheckPos = false;
posX = this.transform.position.x + Random.Range(-3.0f, 3.0f);
posY = this.transform.position.y + Random.Range(-3.0f, 3.0f);
posZ = this.transform.position.z + Random.Range(-3.0f, 3.0f);
if(posX > 3f && posY > 3f){
small_CheckPos = true;
}
if (small_CheckPos == true) {
Vector3 newPos = new Vector3(posX, posY, posZ);
GameObject createdObject = GameObject.Instantiate(smallRock_Prefab,
newPos, spawn_LocationForSmall.rotation) as GameObject;
createdObject.transform.parent = spawn_LocationForSmall.transform;
}
}
smallCount = true;
}
/* the other two functions are similar to this */
I don't really know if you can change the gravity for each individual, but you can change these things:
Mass:
In the Rigidbody component, there is a "Mass" components at the top. As in the Unity Documentation says: "Higher mass objects push lower mass objects more when colliding. Think of a big truck, hitting a small car." However, it doesn't change how fast an object falls.
Physics Material:
In the Collider components, you should see something called "Material". You can create new physics materials and edit them randomly to make the friction between the rock and the surface higher or lower, and change the bounciness of rocks that way.
Constant Force:
If you want some objects to fall faster, you might want to use this component. I personally never used this before, but it looks great for your problem. You can add a constant force to an object with this component, so if you add some downwards force on your rocks it should help them get down faster.
Please let me know if any of these helped.
Search for Particle Systems :
1) https://docs.unity3d.com/ScriptReference/ParticleSystem.html
2) https://www.youtube.com/watch?v=FEA1wTMJAR0&t=536s
3) https://www.youtube.com/watch?v=xenW67bXTgM
It allows you to upload cool effects or even prefabs as the clone objects (in this case rocks/asteroids). Its also able to control the spawning speed/ amount/ velosity/ (random)size/ physics(gravity)
Related
I have a script for that Instantiates 2 game object but when something happens to one of them it also happens to the another one even when the conditions are not met for it. How can I make the script act separately for every Game Object?
GO script:
private Transform target;
public float speed = 2f;
private Animator anim;
public float H2Damage = 5f;
private healthBar Hp;
void Start()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
anim = gameObject.GetComponent<Animator>();
Hp = GameObject.FindGameObjectWithTag("enemy").GetComponentInChildren<healthBar>();
}
void Update()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
if (Hp.Died == true)
{
Hp.Died = false;
anim.SetBool("Hero2Attack", false);
anim.SetBool("Hero2Move", true);
}
if (!target || this.anim.GetCurrentAnimatorStateInfo(0).IsName("Hero2ATTACK"))
return;
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
anim.SetBool("Hero2Move", true);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("enemy"))
{
anim.SetBool("Hero2Attack", true);
healthBar hp = col.transform.GetComponent<healthBar>();
hp.GiveDamage(H2Damage);
}
}
I believe they act the same way because they are getting the same GetComponentWithTag(), so they will Get the same objects. You also instantiate the animator, which is the exact same one, so they will do the same things. --> If it was as simple as changing the position by 1 meter per second, they would have different behavior (ie. be at different positions) But.... If you instantiate it, the current position is also instantiated, so you are going to have to get the current postion and set it to what you want, so both of these moving boxes aren't at the exact same position. You will have to do something similar here, you are going to want to either (create different scripts for the objects, or set the script values to different things)
TL;DR: You are copying the exact same script with the exact same components, so they will act similarly.
Fix: The only way to fix this is by setting their values after you instantiate them using GetComponent<>()or changing the script you assign them. (or any components for that matter)
Let me know if you have any problems or questions in the comments! :)
I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}
I sticked my character to an empty object and applied Rigidbody, script to that empty object(To change the pivot); nevertheless, the character is not moving with this code:
public class main_character : MonoBehaviour
{
public float speed;
private float max_vel = 4f;
private bool grounded = true;
private bool flip = true;
private Rigidbody2D main_vel;
private void Awake()
{
main_vel = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
float now_vel = Mathf.Abs(main_vel.velocity.x);
if (Input.GetKey("d") && flip)
{
flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(speed, 0f) * Time.deltaTime);
}
}
if(Input.GetKey("a") && !flip)
{
!flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(-speed, 0f) * Time.deltaTime);
}
}
}
void flip_side()
{
flip = !flip;
transform.Rotate(0f, 180f, 0f);
}
}
you either need to add a speed value to the top of the class, or dynamically generate it. I would also put an exception or debug that catches when you press the button while speed is set to zero.
if speed accelerates you're going to need to build it or call it in update and give it an initial speed + speed based on delta time. I would also clamp it to max speed there instead, as it prevents large values being tracked by the game engine.
--Regarding an empty parent situation, If there is no physics components, the child should retain the motion of it's parent. That being said, you must have a rigidbody to add force to. You look like you might be using 2d so I have less experience with those rigidbodies, but I imagine it would have to not be kinematic to accept force as you are doing. After checking for that, I would maybe add some debug that posts your vector2 and speed from inside your max_vel check.
I have a setup similar to this mockup in my 3D Unity game.
Each object has a collider as well as a rigid body. I am moving the object using mouse using simple mouse input events
private void OnMouseDown()
{
isSelected = true;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 touchLocation = InputController.Instance.getTapLocation(); // Gets touch/mouse location using Raycast
_transform.position = track.GetPositionOnPath(touchLocation); //Uses vector projection math
}
private void OnMouseUp()
{
if (isSelected)
isSelected = false;
}
The object movement is constrained to a path using Vector projection
public Vector3 GetPositionOnPath(Vector3 touchLocation)
{
//Make the vector for the track u
Vector3 u = end.position - start.position;
//Make the touch location vector from the track start position v
Vector3 v = touchLocation - start.position;
float uv = Vector3.Dot(u, v);
float uMagSqr = Mathf.Pow(u.magnitude, 2);
Vector3 p = (uv / uMagSqr) * u; //Projection Formula (u.v)/(|u|^2) * u
Vector3 finalVector = start.position + p;
//Clamp the final vector to the end.positions of the track
if (start.position.x > end.position.x)
finalVector.x = Mathf.Clamp(finalVector.x, end.position.x, start.position.x);
else
finalVector.x = Mathf.Clamp(finalVector.x, start.position.x, end.position.x);
if (start.position.z > end.position.z)
finalVector.z = Mathf.Clamp(finalVector.z, end.position.z, start.position.z);
else
finalVector.z = Mathf.Clamp(finalVector.z, start.position.z, end.position.z);
return finalVector;
}
I want to make it in such a way that other objects in the way would obstruct the movement of the selected object as shown in the mockup. The solutions I thought of were to increase the weights of the objects so that they won't fly away when they collide, or to keep every object kinematic except the currently selected one. But they both have the same issue that the objects simply slide through each other.
It seems like a simple thing to do, but I can't seem to find any resources to help me out. I would really appreciate some help on how to tackle this problem and implement the mechanic as I intend. Thanks in Advance.
I have yet to find the exact solution I am looking for. But since it is highly discouraged to move the position of a rigid body object, I switched over to a physics-based movement based on information I found on this and this thread. Here is my current implementation:
private void OnMouseDown()
{
isSelected = true;
_rigidbody.isKinematic = false;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 target = InputController.Instance.getTapLocation();
//_rigidbody.MovePosition(target);
force = _track.GetPositionOnPath(target) - _transform.position;
}
private void OnMouseUp()
{
if (!isSelected) return;
isSelected = false;
_rigidbody.isKinematic = true;
force = Vector3.zero;
}
private void FixedUpdate()
{
_rigidbody.velocity = force;
}
However, this comes at the cost of the movement not being perfectly responsive to the input given by the player as shown in this gif.
This is a fix so I am posting this as an answer but any other better options are much appreciated.
My game is a 2D RTS, and I was wondering if anyone knew of a good tutorial for Unity, or if someone well-versed in the syntax of it could tell me what I could have done wrong.
So, I have my camera object, and my player object, both tagged. The player object just has a sprite on it, and is set to rigidbody. The script goes as follows:
using UnityEngine;
using System.Collections;
public class AIsciript : MonoBehaviour
{
private bool thisIsPlayer = true;
private GameObject objPlayer;
private GameObject objCamera;
//input variables (variables used to process and handle input)
private Vector3 inputRotation;
private Vector3 inputMovement;
//identity variables (variables specific to the game object)
public float moveSpeed = 100f;
// calculation variables (variables used for calculation)
private Vector3 tempVector;
private Vector3 tempVector2;
// Use this for initialization
void Start()
{
objPlayer = (GameObject)GameObject.FindWithTag("Player");
objCamera = (GameObject)GameObject.FindWithTag("MainCamera");
if (gameObject.tag == "Player")
{
thisIsPlayer = true;
}
}
// Update is called once per frame
void Update()
{
FindInput();
ProcessMovement();
if (thisIsPlayer == true)
{
HandleCamera();
}
}
void FindInput()
{
if (thisIsPlayer == true)
{
FindPlayerInput();
}
else
{
FindAIInput();
}
}
void FindPlayerInput()
{
//find vector to move
inputMovement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
//find vector to the mouse
tempVector2 = new Vector3(Screen.width * 0.5f, 0, Screen.height * 0.5f);
// the position of the middle of the screen
tempVector = Input.mousePosition;
// find the position of the mouse on screen
tempVector.z = tempVector.y;
tempVector.y = 0;
Debug.Log(tempVector);
inputRotation = tempVector - tempVector2;
}
void FindAIInput()
{
}
void ProcessMovement()
{
rigidbody.AddForce(inputMovement.normalized * moveSpeed * Time.deltaTime);
objPlayer.transform.rotation = Quaternion.LookRotation(inputRotation);
objPlayer.transform.eulerAngles = new Vector3(0, transform.eulerAngles.y + 180, 0);
objPlayer.transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
void HandleCamera()
{
objCamera.transform.position = new Vector3(transform.position.x, 15, transform.position.z);
objCamera.transform.eulerAngles = new Vector3(90, 0, 0);
}
}
I just figured I would post the code just in case, but I figure it's probably not the issue, as I tried to force it to move in Start() and nothing happened.
You shouldn't be using all those checks for thisIsPlayer. You should have separate classes for the player entity and non-player entities.
Public variables are exposed in the editor and get serialized with the entity when the level is saved. This could mean that moveSpeed is not currently set to what it is initialized to in this class.
You shouldn't add force to a rigidbody in the Update method. There's a FixedUpdate method that's used for applying physics. This is because Update is called once per frame, no matter what the framerate, and FixedUpdate is only called at specific intervals, so physics forces aren't affected by framerate.
Also, you shouldn't try to apply a force and set the position of the transform of the same object. Strange things will happen.
If you go into the Unity Asset Store (available in the Window menu within Unity) there is a section called "Complete Projects" that contains some free tutorials. I can't remember which of them is written in C#, but even the JavaScript ones will give you some ideas on how to structure the project.
I don't know if I understood it right: your problem is that it is not moved by the AI?
if this is so, one problem could be that you initialize your
private bool thisIsPlayer = true;
with true but I can not see any condition setting it to false (enter ai mode)
just my 2 cents :)