Move 3d models in Unity 3d - c#

In unity 3d, I've create an Empty GameObject that Instanciates a simple 3d model when i click with the mouse ( 3d model imported from blender).
I've written a simple script that moves the EmptyGameObject using
transform.Translate(speed * Time.deltaTime, 0, 0);
But when i run the project, only the Empty Game Object moving but I need that the 3d model moving to x asix. How can i resolve this problem? Thanks for the help.
Moving script (putting on EmptyGameObject):
public float speed = 0f;
// Start is called before the first frame update
void Start()
{
speed = 0f;
}
// Update is called once per frame
void Update()
{
transform.Translate(speed * Time.deltaTime, 0, 0);
if (Input.GetMouseButtonDown(0)) {
speed += speed + 0.5f;
}
}
Script for instantiated objects:
public GameObject model;
public GameObject model1;
public int counter = 0;
void Start(){
model= Instantiate(model, transform.position, Quaternion.identity); //spawn model
}
void Update(){
if (Input.GetMouseButtonDown(0)) {
ChangeModel();
}
}
void ChangeModel(){
counter++;
switch (counter) {
case 1:
Destroy(model);
Destroy(model1);
model = (GameObject) Instantiate(model, transform.position, Quaternion.identity);
break;
case 2:
Destroy(model);
model1= (GameObject) Instantiate(model1, transform.position, Quaternion.identity);
counter =0;
break;
}
}

In general all you need to do is spawn the new objects always as children of this EmptyGameObject. Instantiate therefore has an overload taking an additional parent Transform reference to spawn the prefab as child to.
As I understand the second script is also attached to the EmptyGameObject so your target parent is simply transform (the Transform component of the GameObject this script is attached to):
//spawn model as child of given Transform
model = Instantiate(prefab, transform.position, Quaternion.identity, transform);
Also note that a cast to (GameObject) is redundant since Instantiate anyway returns the type of the given prefab.
However
I do not really understand your usage of model, model1 and Instantiate and Destroy here ...
What do you destroy the model for if you want to directly respawn it anyway?
In general I think you would be way better also performance wise spawning them both once and only activate and deactivate according models:
public GameObject prefab;
public GameObject prefab1;
void Start()
{
model = Instantiate(prefab, transform.position, Quaternion.identity, transform);
model1 = Instantiate(prefab1, transform.position, Quaternion.identity, transform);
model1.SetActive(false);
}
void ChangeModel()
{
counter = (counter + 1) % 2;
model.SetActive(counter == 0);
model1.SetActive(counter == 1);
}
That's it. If you simply already place the two prefabs into the EmptyGameObject right from the beginning you don't need the Instantiate at all ...
And btw in case you only have two states anyway instead of a counter you could also use a simple bool here:
bool isModel1;
private void ChangeModel()
{
isModel1 = !isModel1;
model.SetActive(!isModel1);
model1.SetActive(isModel1);
}
And finally note that from your movement script:
speed += speed + 0.5f;
this results in
speed = speed + speed + 0.5f;
but I guess what you rather want is
speed += 0.5f;
which results in
speed = speed + 0.5f;

Related

Unity2D move object up and back down with smooth transition

(I'm a beginner so please have patience with me).
How it needs to happend:
I have a player that can jump and hit a tile, on collision the tile with move a fixed distance up and come back down with a smooth transition.
What I have so far:
I am detecting the collision now there's only the matter of moving the tile up and down.
The catch
The tiles are suspended in air so basically they either don't have a RigidBody2D or they have one with gravity scale 0
What I've tried
Basically I've tried 2 solutions:
(I'm not limited to these 2, I want to implement the solution that is correct so I am open to other ideas)
I was thinking to simply take the current position, calculate another vector with another position, and lerp to the new position and then lerp back to the initial one.
Vector2 initialPosition = transform.position;
Vector2 targetPosition = new Vector2(initialPosition.x + 4f, initialPosition.y + 4f);
print("Initial position: " + initialPosition);
print("Target position: " + targetPosition);
Vector2.Lerp(initialPosition, targetPosition, 1f);
I tried adding a rigidbody with scale 0
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
Jumping = false;
anim.SetInteger("State", 0);
}
// print("Colision layer: " + collision.collider.gameObject.layer);
if (collision.collider.gameObject.layer == Mathf.Log(layerMask.value, 2))
{
GameObject Tile = collision.gameObject;
Rigidbody2D rigidBody = Tile.GetComponent<Rigidbody2D>();
rigidBody.gravityScale = 1;
rigidBody.AddForce(new Vector2(0, 10f), ForceMode2D.Force);
StartCoroutine(MoveTileWithForce(rigidBody));
}
}
IEnumerator MoveTileWithForce(Rigidbody2D TileRigidBody)
{
yield return new WaitForSeconds(1);
TileRigidBody.AddForce(new Vector2(0, -5f), ForceMode2D.Force);
TileRigidBody.gravityScale = 0;
print("END MY COROUTINE, gravityScale: " + TileRigidBody.gravityScale);
}
I think the best way you can achieve this is in this simple way:
1
Make sure you have a box collider for your character and a Rigidbody and the same for the block you wanna move. For both of the colliders set on trigger. If you want to have another collider for the player in order to make him touch the ground or hit enemies you can add another box collider, but make sure you have one on trigger on his head
2
Add a script to the block you wanna move and also a tag to the player, for example "player"
3
Inside of this new script check if the block is triggering the player:
void OnTriggerEnter2D(Collision other)
{
if(other.compareTag("player"))
{ StartCoroutine(MovingBlock(0.5f, transform.position, upperPosition));}
|
It will enter inside the if when the player touches the block, it will start a coroutine that I will now write so you will understand the 0.5f and the other variables
IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
float elapsedTime = 0;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(startpos, endpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
elapsedTime = 0f;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(endpos, startpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
}
Basically, the Coroutine will move the object from startpos to endpos and then back again in the amount of time that you decide (in this case 0.5 seconds). I have used the Vector2 variable upperposition and it's just the height you want to reach with the platform.
If you want, you can also add inside the coroutine some yield return new WaitForSeconds(timeToWait) and make the platform wait in a certain position the amount of seconds you want (timeToWait)
Declare Vector2 initialPosition at the beginning of your class.
On Start() of the tile object you should get the initialPosition = transform.position.
And in the OnCollisionEnter2D(Collision2D collision) you could start a coroutine to bring the tile back down.
So for example:
Player Script:
public class Player : MonoBehaviour
{
public Rigidbody2D rb;
//Initialize the rigidbody on the editor.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(new Vector2(0f, 1000f));
}
}
}
And the tile script:
public class MyTile : MonoBehaviour
{
Vector2 initialPosition;
float speed = 2f; //the speed the tile will go down.
private void Start()
{
initialPosition = transform.position;
}
private void OnCollisionEnter2D(Collision2D collision)
{
//The amount you want to go up on the Y component, for this example I used 2.
transform.position = new Vector2(transform.position.x, transform.position.y + 2f);
StartCoroutine(MoveTileDown());
}
IEnumerator MoveTileDown()
{
while (transform.position.y > initialPosition.y)
{
transform.position = Vector2.Lerp(transform.position, initialPosition, Time.deltaTime * speed);
yield return null; //Make it run every frame, just like Update()
}
StopCoroutine(MoveTileDown());
}
}
There are a lot of ways you can achieve the same result, this is just one of them.

How to make a Rigidbody2D bullet move in Unity2D

I've been trying various fixes for days now and I cannot figure this out. I need a bullet to spawn and move until collision, but I cannot even get it to move (it spawns just fine). Note: This is a 2D game.
Here is my code:
public class Enemy : MonoBehaviour
{
public float timeBetweenFire = 2;
public Rigidbody2D bullet;
public Transform gunSpawn;
private float timer;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update() {
timer+=Time.deltaTime;
if (timer >= timeBetweenFire) {
Shoot();
timer = 0;
}
}
private void Shoot() {
Rigidbody2D bulletInstance;
bulletInstance = Instantiate(bullet, gunSpawn.position, gunSpawn.rotation) as Rigidbody2D;
//bulletInstance.AddForce(gunSpawn.forward * 1000f);
//bulletInstance.AddForce(Vector3.up * 10 * Time.deltaTime);
//bulletInstance.AddForce(transform.forward * 100);
bulletInstance.AddForce(Vector3.up * 1000);
}
}
The commented bulletInstance methods are what I've recently tried, I did a bunch of large scale code changes regarding bullet (iirc changed it around between Rigidbody, GameObject, and Transform) and nothing helped. The bullet spawns a good bit away from anything except the gunSpawn, which is just an empty GameObject, so I don't think collisions are an issue. I'm a new Unity programmer so forgive me for any dumb mistakes.
UPDATE:
This is what I did to get it (mostly) working. When the bullet collides it can spin out, but at least I made some progress.
public class Deflect : MonoBehaviour
{
private Rigidbody2D bullet;
public float bulletSpeed = 0.1f;
Vector2 direction = new Vector3(0.7f,0.7f);
ContactPoint2D[] myContact = new ContactPoint2D[1];
Vector2 _velocity;
public void Update ()
{
}
// Start is called before the first frame update
void Start()
{
bullet = this.GetComponent<Rigidbody2D>();
_velocity = bullet.velocity;
}
void OnCollisionEnter2D(Collision2D collision)
{
foreach (ContactPoint2D contact in collision.contacts)
{
print(contact.collider.name + " hit " + contact.otherCollider.name);
// Visualize the contact point
Debug.DrawRay(contact.point, contact.normal, Color.white);
}
Vector2 inNormal = collision.contacts[0].normal;
_velocity = Vector3.Reflect(_velocity, inNormal);
bullet.velocity = _velocity;
}
}
1 - Create your bullet prefab as a GameObject
2 - Add a RigidBody2D to it
3 - Make sure the isKinematic is unchecked on the Rigibody2d
4 - then
GameObject bulletInstance = Instantiate(bullet, gunSpawn.position,gunSpawn.rotation);
bulletInstance.GetComponent<Rigidbody2D>().AddForce(Vector3.up * 1000);
IsKinematic prevents the Rigidbody2D to be affected by forces .
https://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html
Make sure that the bullet has a rigid-body component and that it is not set to kinematic.

How can I apply scripts in new camera which is child of a gameobject

Earlier I was facing problem regarding the unity camera problem it always stuck on 0,0,0.08 and also find a solution so I first create an empty gameobject and then drag the camera in that empty gameobject but after doing this the scripts which I applied to the gameobject is working fine but the script which I place in camera is not working at all
Camera Script
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
void Update()
{
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
}
Player Script
public float speed = 4.5f;
public float JumpingForcec = 450f;
void Update()
{
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
First of all when dealing with a Rigidbody (or the Physics in general) you shouldn't set a position directly through the Transform component but rather use Rigidbody.position or in your case for a smooth movement even rather Rigidbody.MovePosition, both in FixedUpdate.
In general anything related to the Physics (so also everything using Rigidbody) should be done in FixedUpdate while the check for GetKeyDown has to be done in Update.
PlayerScript
public class PlayerScript : MonoBehaviour
{
public float speed = 4.5f;
public float JumpingForcec = 450f;
// If possible reference this in the Inspector already
[SerializeField] private Rigidbody rigidBody;
private bool jumpWasPressed;
private void Awake()
{
if (!rigidBody) rigidBody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
rigidBody.MovePosition(transform.position + speed * Vector3.forward * Time.deltaTime);
if (!jumpWasPressed) return;
Debug.Log("SPace was pressed");
rigidBody.AddForce(Vector3.up * JumpingForcec);
jumpWasPressed = false;
}
private void Update()
{
// Note that currently you can multijump .. later you will want to add
// an additional check if you already jump again
if (Input.GetKeyDown(KeyCode.Space)) jumpWasPressed = true;
}
}
Make sure that Is Kinematic is disabled in the Rigidbody component! Otherwise AddForce is not processed.
If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore.
The camera movement I would move to LateUpdate in order to make sure it is the last thing calculated after the other Update calls have finished. Especially after all user input has been processed (in your case maybe not that relevant since movement is processed in FixedUpdate but in general).
Second problem: Here you are not taking the changed Y position by jumping into account so rather add the "wobbling" effect to the player's transform.position.y and rather use the localPosition for the Camera:
public class CameraScript : MonoBehaviour
{
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
// reference the player object here
public Transform playerTransform;
private float originalLocalPosY;
private void Start()
{
if(!playerTransform) playerTransform = transform.parent;
originalLocalPosY = transform.localPosition.y;
}
private void LateUpdate()
{
transform.localPosition = Vector3.up * (originalLocalPosY + Mathf.Cos(playerTransform.position.z * MovementFrequency) * MovementAmplitude);
}
}
Maybe you want to disable the wobbling effect during a jump later, though ;)
Try to put all the update stuff in the same method, it should work both (theorically, not tested) so you have to fix your code in order to get what you want:
void Update() {
// Camera update
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
// Player update
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
Hope this helps you, cheers!

Unity3D C# Object Movement

So I am a bit of a noob when it comes to scripting/programming and I am in need of some assistance. Currently I have the script below attached to an empty game object that is in the middle of a sphere. The empty object has a child cube that is on the outside of the sphere so when the empty object rotates around, the cube moves along the outside of the sphere.
My problem comes when that cube comes in contact with any other immovable object on the outside of the sphere, it moves and rotates around that object which is what I dont want. What I want is for the cube to stop moving in the direction the immovable object is but be able to move left/right/back. I have written code that when the cube comes in contact with the immovable object it stops the script that rotates the empty game object, but then I cant move the empty game object ever again...its just frozen there. Here is my script.
This is the script attached to the empty game object
public class SphereMotor : MonoBehaviour {
public Vector3 eulerAngleVelocity;
public Rigidbody rb;
private float speed = 45.0f;
public bool collided = false;
public float turnSpeed = 45.0f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
public void Collided()
{
collided = true;
}
public void NotCollided()
{
collided = false;
}
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow) && !collided) {
transform.Rotate (0.0f, turnSpeed * Time.deltaTime, 0.0f);
}
if (Input.GetKey(KeyCode.RightArrow) && !collided) {
transform.Rotate (0.0f, -turnSpeed * Time.deltaTime, 0.0f);
}
if (Input.GetKey(KeyCode.UpArrow) && !collided) {
transform.Rotate (turnSpeed * Time.deltaTime, 0.0f, 0.0f);
}
if (Input.GetKey(KeyCode.DownArrow) && !collided) {
transform.Rotate (-turnSpeed * Time.deltaTime, 0.0f, 0.0f);
}
}
This is the script for collision
herebool isColliding = false;
public SphereMotor _SphereMotor;
void Start()
{
_SphereMotor = GameObject.FindObjectOfType<SphereMotor>();
}
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "Player")
{
//isColliding = true;
StartCoroutine(ObjectColliding());
Debug.Log("wall hit");
}
}
void OnCollisionExit(Collision collision)
{
if(collision.gameObject.tag == "Player")
{
isColliding = false;
}
}
public IEnumerator ObjectColliding()
{
isColliding = true;
yield return new WaitForSeconds(2f);
isColliding = false;
}
void Update()
{
if(isColliding == true)
_SphereMotor.Collided();
if(isColliding == false)
_SphereMotor.NotCollided();
}
Here is a video showcasing my problem in action: https://www.youtube.com/watch?v=tkbbiTwkTqA
I tried using a Coroutine to try and fix the problem but it doesnt really work. When I hold one of the movement buttons down the cube still rotates around the immovable object. I dont really know how to approach this problem so any advice would be really appreciated!
One way would be to use collision.relativeVelocity or collision.contacts to detect from which direction the collision came, then to save some flags like collidedLeft, collidedRight, and so something similar to what you do now - on update do not rotate in some direction if one of the flags is true. But then, what it comes in some angle? You basically need to implement the whole physics system.
I would suggest a little different configuration to achieve this, that uses built in physics engine.
First, it is generally a bad practice to use transform.rotation on objects that affect rigidbodies, as it requires extra computation. It is better to work with rigidbody directly in FixedUpdate.
Now, both the inner sphere and the cube are rigidbodies. We do not need gravity or angular drag. Instead of parenting them, a better way is to connect them using fixed joint.
Next, in inner spere's rigidbody properties, make its position fixed on X, Y and Z axis (we want it to rotate only, not to move).
The cube needs a collider. Inner shpere doesnt.
And now you only need the SphereMotor, no need for collision handling at all, all is done by rigidbodies. The code is very similar to yours, just manipulates rigidbody speed instead of doing it manually:
using UnityEngine;
public class SphereMotor : MonoBehaviour {
public Rigidbody rb;
public float turnSpeed = 45.0f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
rb.freezeRotation = false;
if (Input.GetKey(KeyCode.LeftArrow)) {
// Unity measures angular velocity in radians
rb.angularVelocity = new Vector3(0.0f, turnSpeed*Mathf.Deg2Rad, 0.0f);
} else if (Input.GetKey(KeyCode.RightArrow)) {
rb.angularVelocity = new Vector3 (0.0f, -turnSpeed*Mathf.Deg2Rad, 0.0f);
} else if (Input.GetKey(KeyCode.UpArrow)) {
rb.angularVelocity = new Vector3 (turnSpeed*Mathf.Deg2Rad, 0.0f, 0.0f);
} else if (Input.GetKey(KeyCode.DownArrow)) {
rb.angularVelocity = new Vector3 (-turnSpeed*Mathf.Deg2Rad, 0.0f, 0.0f);
} else {
rb.freezeRotation = true; // No key pressed - stop
}
}
}
I create a demo project, you can get it from my dropbox here:
https://www.dropbox.com/s/b1kjrax0repku48/SphereSampleProject.zip?dl=0
Hope that helps

Unity 3D How to setup Turret Auto Aim? C#

what i't trying to achieve is have my turrent rotate and follow an "Enemy".
At the moment it detects the enemy but it is not rotating and I don't know why.
The bullet it a prefab i drag in, there has to be a better way to do this? Anyone have any suggestions please?
At the moment the bullet never triggers, but the log Shoot and does...and the rotate doesn't work.
Here is what i have
using UnityEngine;
using System.Collections;
public class TurretController : MonoBehaviour {
public Rigidbody bulletPrefab;
private Transform target;
private GameObject bullet;
private float nextFire;
private Quaternion targetPos;
void OnTriggerEnter(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("in");
target = otherCollider.transform;
StartCoroutine ("Fire");
}
}
void OnTriggerExit(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("out");
target = null;
StopCoroutine("Fire"); // aborts the currently running Fire() coroutine
}
}
IEnumerator Fire()
{
while (target != null)
{
nextFire = Time.time + 0.5f;
while (Time.time < nextFire)
{
// smooth the moving of the turret
targetPos = Quaternion.LookRotation (target.position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetPos, Time.deltaTime * 5);
yield return new WaitForEndOfFrame();
}
// fire!
Debug.Log ("shoot");
bullet = Instantiate(bulletPrefab, transform.position, transform.rotation) as GameObject;
//bullet.rigidbody.velocity = transform.forward * bulletSpeed;
}
}
}
I tried to change the instantiate part by using this instead
bullet = (GameObject)Instantiate(bulletPrefab, transform.position, transform.rotation);
bullet.GetComponent<Bullet>().target = target.transform;
But then i just get errors like "InvalidCastException: Cannot cast from source type to destination type.
TurretController+c__Iterator0.MoveNext () (at Assets/Scripts/TurretController.cs:44)"
BTW, here's the turret rotation code I used in my project (shared with permission):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Turret : MonoBehaviour
{
[SerializeField]
private float turnRateRadians = 2 * Mathf.PI;
[SerializeField]
private Transform turretTop; // the gun part that rotates
[SerializeField]
private Transform bulletSpawnPoint;
private Enemy target;
void Update()
{
TargetEnemy();
}
void TargetEnemy()
{
if (target == null || target.health <= 0)
target = Enemy.GetClosestEnemy(turretTop, filter: enemy => enemy.health > 0);
if (target != null)
{
Vector3 targetDir = target.transform.position - transform.position;
// Rotating in 2D Plane...
targetDir.y = 0.0f;
targetDir = targetDir.normalized;
Vector3 currentDir = turretTop.forward;
currentDir = Vector3.RotateTowards(currentDir, targetDir, turnRateRadians*Time.deltaTime, 1.0f);
Quaternion qDir = new Quaternion();
qDir.SetLookRotation(currentDir, Vector3.up);
turretTop.rotation = qDir;
}
}
}
class Enemy : MonoBehaviour
{
public float health = 0;
private static HashSet<Enemy> allEnemies = new HashSet<Enemy>();
void Awake()
{
allEnemies.Add(this);
}
void OnDestroy()
{
allEnemies.Remove(this);
}
/// <summary>
/// Get the closest enemy to some transform, optionally filtering
/// (for example, enemies that aren't dead, or enemies of a certain type).
/// </summary>
public static Enemy GetClosestEnemy(Transform referenceTransform, System.Predicate<Enemy> filter=null)
{
// Left as an exercise for the reader.
// Remember not to use Vector3.Distance in a loop if you don't need it. ;-)
// return allEnemies[0];
}
}
First problem: the bullet prefab. The variable type is RigidBody. If you want to treat it as a game object, the variable must be a game object. Or you can instantiate it, cast to RigidBody, then use the .gameObject accessor. Like this:
((RigidBody)Instantiate(theRigidbody)).gameObject
Second problem: start simple with the rotation. If it's not working, don't get fancy yet. Start with something like this (an instant rotation toward the target):
Vector3 targetDirection = target.transform.position - transform.position;
targetDirection.y = 0; // optional: don't look up
transform.forward = targetDirection;
If it works, then add small pieces of additional complexity until it does exactly what you want. And if you don't get things figured out, give me a shout (a comment) on Monday. I've written turret-aiming code (including a maximum rotation speed), and I don't think my boss would mind if I upload it.

Categories