I'm working on a small game: I want all GameObjects to be pulled into the middle of the screen where they should collide with another GameObject.
I tried this attempt:
using UnityEngine;
using System.Collections;
public class Planet : MonoBehaviour
{
public Transform bird;
private float gravitationalForce = 5;
private Vector3 directionOfBirdFromPlanet;
void Start ()
{
directionOfGameObjectFromMiddle = Vector3.zero;
}
void FixedUpdate ()
{
directionOfGameObjectFromMiddle = (transform.position-bird.position).normalized;
bird.rigidbody2D.AddForce (directionOfGameObjectFromMiddle * gravitationalForce);
}
}
sadly I can't get it to work. I've been told that I have to give the object that is being pulled another script but is it possible to do this just with one script that is used on the object that pulls?
So first you have a lot of typos / code that doesn't even compile.
You use e.g. once directionOfBirdFromPlanet but later call it directionOfGameObjectFromMiddle ;) Your Start is quite redundant.
As said bird.rigidbody2D is deprecaded and you should rather use GetComponent<Rigidbody2D>() or even better directly make your field of type
public Rigidbody2D bird;
For having multiple objects you could simply assign them to a List and do
public class Planet : MonoBehaviour
{
// Directly use the correct field type
public List<Rigidbody2D> birds;
// Make this field adjustable via the Inspector for fine tuning
[SerializeField] private float gravitationalForce = 5;
// Your start was redundant
private void FixedUpdate()
{
// iterate through all birds
foreach (var bird in birds)
{
// Since transform.position is a Vector3 but bird.position is a Vector2 you now have to cast
var directionOfBirdFromPlanet = ((Vector2) transform.position - bird.position).normalized;
// Adds the force towards the center
bird.AddForce(directionOfBirdFromPlanet * gravitationalForce);
}
}
}
Then on the planet you reference all the bird objects
On the birds' Rigidbody2D component make sure to set
Gravity Scale -> 0
you also can play with the Linear Drag so in simple words how much should the object slow down itself while moving
E.g. this is how it looks like with Linear Drag = 0 so your objects will continue to move away from the center with the same "energy"
this is what happens with Linear Drag = 0.3 so your objects lose "energy" over time
Related
I am trying to practice creating a clone of Galaga following the same ruleset as the original. I am currently stuck trying to attempt a limit on the amount of cloned prefabs that can be in the scene at any one time, in the same way that Galaga's projectiles are limited to 2 on screen at any time. I want to make it so the player can shoot up to two projectiles, which destroy after 2 seconds or when they collide (this part is functioning), followed by not being able to shoot if two projectile clones are active and not yet destroyed in the hierarchy (Not working as I can instantiate projectiles over the limit of 2).
I have combed through Google for about 3 hours with no solutions that have worked for me, at least in the ways that I had attempted to implement them.
Thank y'all so much for the help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float moveSpeed = 1.0f;
public playerProjectile projectile;
public Transform launchOffset;
public int maxBullets = 0;
private GameObject cloneProjectile;
public Rigidbody2D player;
// Start is called before the first frame update
void Start()
{
player = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
MovePlayer();
PlayerShoot();
}
public void MovePlayer()
{
player.velocity = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * moveSpeed;
}
public void PlayerShoot()
{
if (Input.GetKeyDown(KeyCode.Z))
{
var cloneProjectile = Instantiate(projectile, launchOffset.position, launchOffset.rotation);
maxBullets++;
if (maxBullets >= 3)
{
Destroy(cloneProjectile, 0.1f);
maxBullets --;
return;
}
}
}
}
You could change the logic up a bit. An instance of the playerController class is active as long as the game is active, so it will know and retain the value of 'maxBullets' until you die or exit the program.
So instead, every time you click "z", the first thing you should do is run the check. If the current amount of live projectiles equals the maximum, have the logic 'return' and exit out of the method.
Hi!
i am making something like 8 ball pool in 3d with unity 3d C#.
A is que ball and I know Dir1. I want to calculate Dir2.I am using Raycast to i can get point of contact.
If you really want to go manual
Instead of a Raycast you would rather use a Physics.SphereCast (according to your balls shape) or alternatively you could also use Rigidbody.SweepTest in order to get hit information for if you object would move.
Both provide you with the information
whether something is hit or not (e.g. check via tag if the first thing hit is a wall or another ball)
both give you a RaycastHit which contains detailed information such as
point the exact contact point
distance how far the ball traveled before hitting something (e.g. for calculating the already applied damping/table friction)
normal of the surface you hit so you can calculate your two balls new direction from that
and of course most importantly which object was hit so you can start a new calculation for that ball as well
The rest is The Math and Physics of Billard or Physics of Billard etc and you will find that a complete answer is way to complex for this page ;)
There are way to many things to consider when calculating physics manually and first of all you will need to decide how realistic you want to go actually. There are spins, frictions, jumps and you know in the real world there doesn't exist any fully elastic collision at all ... So boy if you are going to pre-calculate all this by hand you can as well just write your own physics engine ;)
Use the existing physics without calculating yourself at all
Now as a complete alternative approach to all that, which doesn't require you to calculate anything at all:
You could simulate the whole physics!
You could
Store all balls current positions (in order to restore them later)
Use Physics.Simulate in a loop, as condition checking if any sphere has moved between the two calls -> If not (or after certain preview time) then break.
Every step track the positions of all balls
After breaking of the loop reset all positions and velocities
=> You already get all the tracked points for each balls LineRenderer ;)
You could even use the positions in order to move the balls yourself instead of use the physics again to show the actual movement ;)
For large systems this would of course cause immense lag (depends also on your target preview time range). But I'd say for only a limited amount of balls this should be fine.
Here a little code I cluched together in a few minutes (way not perfect of course)
// little helper component for storing some references and identify clearly as a ball
// (rather then allowing to reference just any GameObject)
public class Ball : MonoBehaviour
{
[SerializeField]
private Rigidbody _rigidbody;
public Rigidbody Rigidbody => _rigidbody;
[SerializeField]
private LineRenderer _line;
public LineRenderer Line => _line;
private void Awake()
{
if (!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
if (_line) _line = GetComponentInChildren<LineRenderer>(true);
}
}
and
public class Example : MonoBehaviour
{
// A simple data container for transform values
private class TransformData
{
public Vector3 Position;
public Quaternion Rotation;
private readonly Rigidbody _rigidbody;
public TransformData(Rigidbody rigidbody)
{
_rigidbody = rigidbody;
Update();
}
public void Update()
{
Position = _rigidbody.position;
Rotation = _rigidbody.rotation;
}
}
// all balls
public Ball[] balls;
// the white ball in particular
public Ball whiteBall;
// direction to shoot in
public Vector3 direction;
// force to apply
public float force;
// How far to predict into the future
public float maxPreviewTime = 10;
// shall this be done every frame (careful with performance!)
public bool continousUpdates;
// stores all initial transform values before starting the prediction in order to restore the state later
private readonly Dictionary<Ball, TransformData> initialPositions = new Dictionary<Ball, TransformData>();
// stores all the predicted positions in order - but for now without the information about the exact frame
// for simplicity and performance we only store WHERE the balls move, not exactly WHEN (could change that though if you wish)
private readonly Dictionary<Ball, List<Vector3>> simulatedPositions = new Dictionary<Ball, List<Vector3>>();
private void Awake()
{
// Initialize empty data sets for the existing balls
foreach (var ball in balls)
{
initialPositions.Add(ball, new TransformData(ball.Rigidbody));
simulatedPositions.Add(ball, new List<Vector3>());
}
}
private void Update()
{
// you could call this every frame e.g. in order to see prediction lines while the player changes force and direction
// have performance in mind though!
if (continousUpdates)
{
UpdateLines();
}
// for demo we shoot on space key
if (Input.GetKeyDown(KeyCode.Space))
{
ShootWhiteBall();
}
}
private void ShootWhiteBall()
{
whiteBall.Rigidbody.AddForce(direction.normalized * force, ForceMode.Impulse);
}
[ContextMenu("Update Preview Lines")]
public void UpdateLines()
{
// update all balls initial transform values to the current ones
foreach (var transformData in initialPositions.Values)
{
transformData.Update();
}
// clear all prediction values
foreach (var list in this.simulatedPositions.Values)
{
list.Clear();
}
// disable autosimulation - the API is a bit unclear whether this is required when manually calling "Physics.Simulate"
// so just to be sure
Physics.autoSimulation = false;
// we will track if any transform values changed during the prediction
// if not we break out of the loop immediately to avoid unnecessary overhead
var somethingChanged = true;
// as second break condition we use time - we don't want o get stuck in prediction forever
var simulatedTime = 0f;
// Do the same thing as you would later for shooting the white ball
// preferably even actually use the exact same method to avoid double maintenance
ShootWhiteBall();
while (somethingChanged && simulatedTime < maxPreviewTime)
{
// Simulate a physics step
Physics.Simulate(Time.fixedDeltaTime);
// always assume there was no change (-> would break out of prediction loop)
somethingChanged = false;
foreach (var kvp in simulatedPositions)
{
var ball = kvp.Key;
var positions = kvp.Value;
var currentPosition = ball.Rigidbody.position;
// either this is the first frame or the current position is different from the previous one
var hasChanged = positions.Count == 0 || currentPosition != positions[positions.Count - 1];
if (hasChanged)
{
positions.Add(currentPosition);
}
// it is enough for only one ball to be moving to keep running the prediction loop
somethingChanged = somethingChanged || hasChanged;
}
// increase the counter by one physics step
simulatedTime += Time.fixedDeltaTime;
}
// Reset all balls to the initial state
foreach (var kvp in initialPositions)
{
kvp.Key.Rigidbody.velocity = Vector3.zero;
kvp.Key.Rigidbody.angularVelocity = Vector3.zero;
kvp.Key.Rigidbody.position = kvp.Value.Position;
kvp.Key.Rigidbody.rotation = kvp.Value.Rotation;
}
// apply the line renderers
foreach (var kvp in simulatedPositions)
{
var ball = kvp.Key;
var positions = kvp.Value;
ball.Line.positionCount = positions.Count;
ball.Line.SetPositions(positions.ToArray());
}
// re-enable the physics
Physics.autoSimulation = true;
}
}
as you can see it is not exactly 100% accurate, tbh no sure why but it is probably something that can be tweaked out.
i was working on a simple car controller script and a humvee model i downloaded. the script works as its supposed to but the car wheels are out of place and are spinning crazy. i tried searching everywhere but didn't found any solution.
here's the code:
using UnityEngine;
public class GroundVehicleController : MonoBehaviour {
public void GetInput()
{
m_horizontalInput = Input.GetAxis("Horizontal");
m_verticalInput = Input.GetAxis("Vertical");
}
private void Steer()
{
m_steeringAngle = maxSteerAngle * m_horizontalInput;
frontDriverW.steerAngle = m_steeringAngle;
frontPassengerW.steerAngle = m_steeringAngle;
}
private void Accelerate()
{
frontDriverW.motorTorque = m_verticalInput * motorForce;
frontPassengerW.motorTorque = m_verticalInput * motorForce;
}
private void UpdateWheelPoses()
{
UpdateWheelPose(frontDriverW, frontDriverT);
UpdateWheelPose(frontPassengerW, frontPassengerT);
UpdateWheelPose(rearDriverW, rearDriverT);
UpdateWheelPose(rearPassengerW, rearPassengerT);
}
private void UpdateWheelPose(WheelCollider _collider, Transform _transform)
{
Vector3 _pos = _transform.position;
Quaternion _quat = _transform.rotation;
_collider.GetWorldPose(out _pos, out _quat);
_transform.position = _pos;
_transform.rotation = _quat;
}
private void FixedUpdate()
{
GetInput();
Steer();
Accelerate();
UpdateWheelPoses();
}
private float m_horizontalInput;
private float m_verticalInput;
private float m_steeringAngle;
public WheelCollider frontDriverW, frontPassengerW;
public WheelCollider rearDriverW, rearPassengerW;
public Transform frontDriverT, frontPassengerT;
public Transform rearDriverT, rearPassengerT;
public float maxSteerAngle = 30;
public float motorForce = 50;
}
The error on Unity console is telling you the problem:
UnassignedReferenceException: The variable frontDriverW of
groundVehicleController has not been assigned. You problably need to
assign the frontDriverW variable of the GroundVehicleController script
in the inspector.
Well, when your script runs, it tries to access the field frontDriverW, but since it was never assigned a value, it throws the mentioned exception.
You need to go in the inspector for the object that contains the GroundVehicleController script and add a value for the frontDriverW field. While you're there, make sure all the other fields have values too, because if they don't, those will also cause the same error.
From the pictures you've show us, it is not possible to know in which object you added the GroundVehicleController script, but just check all of the Humvee objects until you find it in the inspector. My guess is that it will probably be in the object Wheels, or the object Humvee.
a) Check the prefab you've created to see if these offsets are there even on the prefab.
b) You're updating the wheel pose with global positions and rotations. this is probably what is causing the offsets. try setting the pose local, or just make the wheel meshes follow whatever the wheel colliders are doing, make sure they're all under the same co-ordinate space. Why not just child each individual wheel to it's corresponding wheel collider? theyre just meshes after all.
c) You haven't assigned your wheel meshes at all in your script.
c) play around with different mass values on the RigidBody (keep it heavy), and also consider attaching a physic material to your wheel colliders to reduce slip and spinning (If the car controller already doesnt have a slip setting)
This is semi complicated of a question but I'll do my best to explain it:
I am making this mobile game in which you have to shoot four cubes. I'm trying to make it so when the cubes are shot by a bullet, they're destroyed and a UI text says 1/4, to 4/4 whenever a cube is shot. But it's being really weird and only counts to 1/4 even when all four cubes are shot and destroyed. I put these two scripts on the bullets (I made two separate scripts to see if that would do anything, it didn't)
And to give a better idea of what I'm talking about, here's a screenshot of the game itself.
I've been using Unity for about 6 days, so I apologize for anything I say that's noob-ish.
EDIT
So I combined the two scripts onto an empty gameobject and here's the new script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManagerScript : MonoBehaviour {
public GameObject cubes;
public Text countText;
public int cubeCount;
public Transform target;
// Use this for initialization
void Start () {
}
void OnTriggerEnter(Collider other)
{
cubes = other.gameObject;
}
// Update is called once per frame
void Update () {
cubes.transform.position = Vector3.MoveTowards(transform.position, target.position, 1f * Time.deltaTime);
if (cubes.gameObject.tag == "BULLET")
{
cubeCount = cubeCount + 1;
countText.text = cubeCount + "/4";
cubes.SetActive(false);
}
}
}
ANOTHER EDIT
I tried everything, so is there a way to detect when all the children in a parent on the Hierarchy are destroyed? Instead of counting up? This can give a better idea:
So I want to be able to detect when Cube, Cube1, Cube2, and Cube3 have all been destroyed.
The answer is pretty simple: Since every individual bullet has that script, each bullet has its own score.
For something like a score you want a single spot to store it, e.g. a script on an empty gameobject that serves as game controller. Just access that in the collision and increase the score (maybe have a look on singletons here).
You can combine those two scripts and actually it might be better to not have this on the bullet, but on the target because there are probably less of them which will save you some performance. (And it does more sense from a logical point of view.)
Edit:
I assume you create the bullets using Instantiate with a prefab. A prefab (= blueprint) is not actually in the game (only objects that are in the scene/hierarchy are in the game). Every use of Instantiate will create a new instance of that prefab with it's own version of components. A singleton is a thing that can only exist once, but also and that is why I mention it here, you can access it without something like Find. It is some sort of static. And an empty gameobject is just an object without visuals. You can easily create one in unity (rightclick > create empty). They are typically used as container and scriptholders.
Edit:
What you want is:
An empty gameobject with a script which holds the score.
A script that detects the collision using OnTriggerEnter and this script will either be on the bullets or on the targets.
Now, this is just a very quick example and can be optimized, but I hope this will give you an idea.
The script for the score, to be placed on an empty gameobject:
public class ScoreManager : MonoBehaviour
{
public Text scoreText; // the text object that displays the score, populate e.g. via inspector
private int score;
public void IncrementScore()
{
score++;
scoreText.text = score.ToString();
}
}
The collision script as bullet version:
public class Bullet : MonoBehaviour
{
private ScoreManager scoreManager;
private void Start()
{
scoreManager = GameObject.FindWithTag("GameManager").GetComponent<ScoreManager>(); // give the score manager empty gameobject that tag
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Target") == true)
{
// update score
scoreManager.IncrementScore();
// handle target, in this example it's just destroyed
Destroy(other.gameObject);
}
}
}
I've recently started learning c# and unity and we're supposed to make a game where a ball rolls around a maze, is in danger of being destroyed by an enemy, and a message pops up when you reach the end. I have most of this done; however, my enemies, who are supposed to move back and forth and destroy the ball when touched, don't work. The walls and floor explodes out when the game is started and I'm not even sure they work at all. In our current assignment, we have to add classes and add another player (which I'm pretty sure I know how to do already). Here's my current code for my enemy class:
using UnityEngine;
using System;
[System.Serializable]
public class Boundary
{
public float xMin, xMax;
}
public class BadGuyMovement : MonoBehaviour
{
public Transform transformx;
private Vector3 xAxis;
private float secondsForOneLength = 1f;
public Boundary boundary;
void Start()
{
xAxis = Boundary;
}
void Update()
{
transform.position = new Vector3(Mathf.PingPong(Time.time, 3), xAxis);
}
void OnTriggerEnter(Collider Player)
{
Destroy(Player.gameObject);
}
}
On lines 21 (xAxis = Boundary;) and 26 (transform.position = new Vector 3) there are errors that I just completely don't understand. If you guys know how to fix it or know of a better way to do something, or at least a better way to move an object back and forth, please let me know!
Thank you so much for taking the time to answer this!
You're getting errors for two reasons, but fairly trivial.
The first error, on line 21 (xAxis = Boundary), you're getting an error because you're assigning a value of type Boundary to a variable of type Vector3.
This is like trying to say an Apple equals an Orange (they don't).
In languages like C#, the types of both the left hand and the right hand of an assignment must be the same.
Simply put
type variable = value; --> Works only if type from the LHS = type from the RHS
Your second error is happening is because you're trying to create a Vector3 with the wrong set of values. The process of creating a type is done through calling the Constructor of the class you're trying to create.
Now, look at the Constructor for a Vector3. One constructor takes 3 parameters of type float and the other takes 2 parameters, again of type float.
You're trying to call the constructor of a Vector3 using a float (from Mathf.PingPong) and a Vector3 (xAxis).
Now that we have that out of the way, try this code.
using UnityEngine;
using System;
[System.Serializable]
public class Boundary
{
public float xMin, xMax;
}
public class BadGuyMovement : MonoBehaviour
{
public Transform transformx;
private Vector3 xAxis;
private float secondsForOneLength = 1f;
public Boundary boundary; //Assuming that you're assigning this value in the inspector
void Start()
{
//xAxis = Boundary; //This won't work. Apples != Oranges.
}
void Update()
{
//transform.position = new Vector3(Mathf.PingPong(Time.time, 3), xAxis); //Won't work, incorrect constructor for a Vector3
//The correct constructor will take either 2 float (x & y), or 3 floats (x, y & z). Let's ping pong the guy along the X-axis, i.e. change the value of X, and keep the values of Y & Z constant.
transform.position = new Vector3( Mathf.PingPong(boundary.xMin, boundary.xMax), transform.position.y, transform.position.z );
}
void OnTriggerEnter(Collider Player)
{
Destroy(Player.gameObject);
}
}