I am creating a player jump function and the collider does not achieve the same height as my players feet on jump.
I am currently trying to manually position the collider based on the position of the player. However, this has caused the capsule to now go way too high and because of the time it takes to fall, the player ends up halfway submerged in the ground.
void Jump() {
if (Input.GetKeyDown(KeyCode.Space)&& isGrounded == true) {
isGrounded = false;
actions.Jump();
rigidbody.AddRelativeForce(Vector3.up * jumpSpeed);
startTime = Time.time;
}
else {
if (transform.position.y < .6f) {
isGrounded = true;
}
}
}
private void Update() {
if (!isGrounded) {
dist = (Time.time - startTime) * capSpeed;
fracOfJourney = dist / journeyLength;
capCollider.center = Vector3.Lerp(start, end, fracOfJourney);
}
else {
// capCollider.center = Vector3.Lerp(end, start, fracOfJourney);
}
}
Rather than calculating the capsule collider position yourself a better solution would be to separate the rendering components of your player from the capsule collider. As a possible solution, you could have in your heirarchy:
Player Root: (rigidbody, player animator, Movement Script)
Renderer: (Mesh of player and renderer here)
Collider: (Capsule Colider here)
After doing this you could match the boundaries of your capsule collider by changing the scale and position as needed during your jumping animation. Separating the collider into it's own gameobject allows you to change the scale without effecting the appearance or position of the player.
I have a script that is applied to several spheres in my scene. I would like to be able to move individual spheres around in the scene by holding them on the screen and dragging around. My first issue is that no matter which sphere you touch and drag, all the spheres move together (they are part of the same prefab but none of them are children of the other spheres). A second issue is that when you touch and drag anywhere on the screen the spheres react, instead of only reacting when you touch on one of the spheres.
Touch touch = Input.GetTouch(0);
for (int i = 0; i < Input.touchCount; i++)
{
if (touch.phase == TouchPhase.Began)
{
screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, screenPoint.z));
}
else if (touch.phase == TouchPhase.Moved)
{
Vector3 cursorPoint = new Vector3(touch.position.x, touch.position.y, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
transform.position = cursorPosition;
}
}
I have a feeling that the issue of all the spheres moving together stems from the fact that the same script is applied to all the spheres, and screenpoint and offest are being derived from gameObject.transform.position, instead of the specific sphere that was touched. I have tried to fix this by putting the following code above the for loop, but I am guessing this might not even be the correct approach. I am new to this :).
RaycastHit hit = new RaycastHit();
Physics.Raycast(touch.position, touch.position, out hit);
if (hit.collider == GameObject.FindGameObjectWithTag("Sphere0"))
{
selectedObject = GameObject.FindGameObjectWithTag("Sphere0");
}
I have my tags set up properly, and if that worked for the first sphere (Sphere0) I would have written the code for the other spheres. But, it seems to not work at all. My project is an ARCore project, but I don't think that should change what I need to code for these aspects of the scene.
Any and all help is greatly appreciated!
You will just want 1 master script separate from you spheres to do the raycasting on touch and sphere controlling. Then tag all your spheres with the same tag (ie. "sphere").
void Update()
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began) // when screen is touched...
{
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenToWorldPoint(touch.position), Vector3.forward, out hit)) // ...cast a ray...
{
if (hit.collider.tag == "sphere") //...and check if ray hits a sphere
{
selectedObject = hit.collider.gameObject;
}
}
}
// add touch controls here and apply to selectedObject for movement
}
I have a Ball(sphere) and a floor in my game scene. Ball.cs is attached to the ball to control its movement in the game(Ball only moves in the vertical direction). Both Ball and floor have colliders attached to them and whenever ball touches the floor, the game should ideally end.
OnCollisionEnter2D Method from the Ball.cs script.
private void OnCollisionEnter2D(Collision2D collision)
{
// Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Update function
void Update () {
//Don't allow control if the bird has died.
if (isDead == false)
{
//Look for input to trigger a "flap".
if (Input.GetMouseButtonDown(0))
{
//...zero out the birds current y velocity before...
rb2d.velocity = Vector2.zero;
// new Vector2(rb2d.velocity.x, 0);
//..giving the bird some upward force.
rb2d.AddForce(new Vector2(0, upForce));
}
}
}
But what's happening is whenever ball touches the ground, it starts rolling on the ground. It moves few units on +X-axis then rolls back and then ultimately stops.
position.X should ideally be 0(as the ball is moving only in Y-axis and it is during the game) but as soon as ball collides with the floor it starts moving.
I am new to Unity and I have no idea what is wrong.
Why is this happening?
EDIT:
Programmer's answer does work but I still don't understand where the horizontal velocity is coming from(there is horizontal velocity component associated with the ball). I need to know why ball is moving in horizontal direction.
I noticed that you are setting isDead to true when collision happens. If you don't want the ball to move again then set velocity to Vector2.zero; in the Update not only in the OnCollisionEnter2D function. Do this only if isDead is true.
void Update()
{
if (isDead)
{
rb2d.velocity = Vector2.zero;
}
}
Another option is to freeze the constraints when the collision happens. If you want the ball to start rolling again then unfreeze it.
private void OnCollisionEnter2D(Collision2D collision)
{
//Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
//Freeze constraints
rb2d.constraints = RigidbodyConstraints2D.FreezeAll;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Execute rb2d.constraints = RigidbodyConstraints2D.None; to unfreeze it after.
I have been working on trying to smooth out my camera motion for my game for days and I cannot get it to a reasonable level of smoothness. Are there any corrections to this code that I can make that will improve the smoothness? I have tried the moveTowards() function as well and it didn't help.
/**************************** CAMERA MOVEMENT *********************************/
void FixedUpdate () {
//Do we need to spawn new platforms yet?
Vector2 playerHeight = playerTransform.position;
if (playerHeight.y > nextPlatformCheck) {
PlatformMaintenaince (); //Spawn new platforms
}
//Update camera position if the player has climbed
Vector2 currentCameraHeight = transform.position;
if (playerTransform.position.y > currentCameraHeight.y)
{
transform.position = Vector2.Lerp(new Vector2(transform.position.x,0.0f), new Vector2(0.0f, playerHeight.y), smooth * Time.deltaTime);
}
else {
// If player is too low, gameover.
if ((playerHeight.y) < (currentCameraHeight.y - 5.5)) {
GameOver();
}
}
}
Use the Update function instead of the FixedUpdate. FixedUpdate only gets called after a certain number od frames.
All the camera movement calculations should be carried out in the LateUpdate (), Let Update() get the player position and then let camera use that position after that Update calculations.
I am new to Unity and I am trying to figure out how to move the camera over a map/terrain using touch input. The camera would be looking down at the terrain with a rotation of (90,0,0). The terrain is on layer 8. I have had no problem getting it moving with keyboard, now I am trying to move to touch and it is very different if you want to keep expected usage on iOS.
The best example I can think of on a built in iOS app is Maps where the user would touch the screen and that point on the map would stay under the finger as long as the finger stayed on the screen. So as the user moves their finger the map appears to be moving with the finger. I have not been able to find examples that show how to do it this way. I have seen may examples of moving the camera or character with the mouse but they don't seem to translate well to this style.
Also posted on Unity3D Answers:
http://answers.unity3d.com/questions/283159/move-camera-over-terrain-using-touch-input.html
Below should be what you need. Note that it's tricky to get a 1 to 1 correspondence between finger/cursor and the terrain when using a perspective camera. If you change your camera to orthographic, the script below should give you a perfect map between finger/cursor position and map movement. With perspective you'll notice a slight offset.
You could also do this with ray tracing but I've found that route to be sloppy and not as intuitive.
Camera settings for testing (values are pulled from the inspector so apply them there):
Position: 0,20,0
Orientation: 90,0,0
Projection: Perspective/Orthographic
using UnityEngine;
using System.Collections;
public class ViewDrag : MonoBehaviour {
Vector3 hit_position = Vector3.zero;
Vector3 current_position = Vector3.zero;
Vector3 camera_position = Vector3.zero;
float z = 0.0f;
// Use this for initialization
void Start () {
}
void Update(){
if(Input.GetMouseButtonDown(0)){
hit_position = Input.mousePosition;
camera_position = transform.position;
}
if(Input.GetMouseButton(0)){
current_position = Input.mousePosition;
LeftMouseDrag();
}
}
void LeftMouseDrag(){
// From the Unity3D docs: "The z position is in world units from the camera." In my case I'm using the y-axis as height
// with my camera facing back down the y-axis. You can ignore this when the camera is orthograhic.
current_position.z = hit_position.z = camera_position.y;
// Get direction of movement. (Note: Don't normalize, the magnitude of change is going to be Vector3.Distance(current_position-hit_position)
// anyways.
Vector3 direction = Camera.main.ScreenToWorldPoint(current_position) - Camera.main.ScreenToWorldPoint(hit_position);
// Invert direction to that terrain appears to move with the mouse.
direction = direction * -1;
Vector3 position = camera_position + direction;
transform.position = position;
}
}
I've come up with this script (I have appended it to the camera):
private Vector2 worldStartPoint;
void Update () {
// only work with one touch
if (Input.touchCount == 1) {
Touch currentTouch = Input.GetTouch(0);
if (currentTouch.phase == TouchPhase.Began) {
this.worldStartPoint = this.getWorldPoint(currentTouch.position);
}
if (currentTouch.phase == TouchPhase.Moved) {
Vector2 worldDelta = this.getWorldPoint(currentTouch.position) - this.worldStartPoint;
Camera.main.transform.Translate(
-worldDelta.x,
-worldDelta.y,
0
);
}
}
}
// convert screen point to world point
private Vector2 getWorldPoint (Vector2 screenPoint) {
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
Pavel's answer helped me a lot, so wanted to share my solution with the community in case it helps others. My scenario is a 3D world with an orthographic camera. A top-down style RTS I am working on. I want pan and zoom to work like Google Maps, where the mouse always stays at the same spot on the map when you pan and zoom. This script achieves this for me, and hopefully is robust enough to work for others' needs. I haven't tested it a ton, but I commented the heck out of it for beginners to learn from.
using UnityEngine;
// I usually attach this to my main camera, but in theory you can attach it to any object in scene, since it uses Camera.main instead of "this".
public class CameraMovement : MonoBehaviour
{
private Vector3 MouseDownPosition;
void Update()
{
// If mouse wheel scrolled vertically, apply zoom...
// TODO: Add pinch to zoom support (touch input)
if (Input.mouseScrollDelta.y != 0)
{
// Save location of mouse prior to zoom
var preZoomPosition = getWorldPoint(Input.mousePosition);
// Apply zoom (might want to multiply Input.mouseScrollDelta.y by some speed factor if you want faster/slower zooming
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize + Input.mouseScrollDelta.y, 5, 80);
// How much did mouse move when we zoomed?
var delta = getWorldPoint(Input.mousePosition) - preZoomPosition;
// Rotate camera to top-down (right angle = 90) before applying adjustment (otherwise we get "slide" in direction of camera angle).
// TODO: If we allow camera to rotate on other axis we probably need to adjust that also. At any rate, you want camera pointing "straight down" for this part to work.
var rot = Camera.main.transform.localEulerAngles;
Camera.main.transform.localEulerAngles = new Vector3(90, rot.y, rot.z);
// Move the camera by the amount mouse moved, so that mouse is back in same position now.
Camera.main.transform.Translate(delta.x, delta.z, 0);
// Restore camera rotation
Camera.main.transform.localEulerAngles = rot;
}
// When mouse is first pressed, just save location of mouse/finger.
if (Input.GetMouseButtonDown(0))
{
MouseDownPosition = getWorldPoint(Input.mousePosition);
}
// While mouse button/finger is down...
if (Input.GetMouseButton(0))
{
// Total distance finger/mouse has moved while button is down
var delta = getWorldPoint(Input.mousePosition) - MouseDownPosition;
// Adjust camera by distance moved, so mouse/finger stays at exact location (in world, since we are using getWorldPoint for everything).
Camera.main.transform.Translate(delta.x, delta.z, 0);
}
}
// This works by casting a ray. For this to work well, this ray should always hit your "ground". Setup ignore layers if you need to ignore other colliders.
// Only tested this with a simple box collider as ground (just one flat ground).
private Vector3 getWorldPoint(Vector2 screenPoint)
{
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
}
Based on the answer from Pavel, I simplified the script and removed the unlovely "jump" when touch with more then one finger and release the second finger:
private bool moreThenOneTouch = false;
private Vector3 worldStartPoint;
void Update() {
Touch currentTouch;
// only work with one touch
if (Input.touchCount == 1 && !moreThenOneTouch) {
currentTouch = Input.GetTouch(0);
if (currentTouch.phase == TouchPhase.Began) {
this.worldStartPoint = Camera.main.ScreenToWorldPoint(currentTouch.position);
}
if (currentTouch.phase == TouchPhase.Moved) {
Vector3 worldDelta = Camera.main.ScreenToWorldPoint(currentTouch.position) - this.worldStartPoint;
Camera.main.transform.Translate(
-worldDelta.x,
-worldDelta.y,
0
);
}
}
if (Input.touchCount > 1) {
moreThenOneTouch = true;
} else {
moreThenOneTouch = false;
if(Input.touchCount == 1)
this.worldStartPoint = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
}
}
using UnityEngine;
// I usually attach this to my main camera, but in theory you can attach it to any object in scene, since it uses Camera.main instead of "this".
public class CameraMovement : MonoBehaviour
{
private Vector3 MouseDownPosition;
void Update()
{
// If mouse wheel scrolled vertically, apply zoom...
// TODO: Add pinch to zoom support (touch input)
if (Input.mouseScrollDelta.y != 0)
{
// Save location of mouse prior to zoom
var preZoomPosition = getWorldPoint(Input.mousePosition);
// Apply zoom (might want to multiply Input.mouseScrollDelta.y by some speed factor if you want faster/slower zooming
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize + Input.mouseScrollDelta.y, 5, 80);
// How much did mouse move when we zoomed?
var delta = getWorldPoint(Input.mousePosition) - preZoomPosition;
// Rotate camera to top-down (right angle = 90) before applying adjustment (otherwise we get "slide" in direction of camera angle).
// TODO: If we allow camera to rotate on other axis we probably need to adjust that also. At any rate, you want camera pointing "straight down" for this part to work.
var rot = Camera.main.transform.localEulerAngles;
Camera.main.transform.localEulerAngles = new Vector3(90, rot.y, rot.z);
// Move the camera by the amount mouse moved, so that mouse is back in same position now.
Camera.main.transform.Translate(delta.x, delta.z, 0);
// Restore camera rotation
Camera.main.transform.localEulerAngles = rot;
}
// When mouse is first pressed, just save location of mouse/finger.
if (Input.GetMouseButtonDown(0))
{
MouseDownPosition = getWorldPoint(Input.mousePosition);
}
// While mouse button/finger is down...
if (Input.GetMouseButton(0))
{
// Total distance finger/mouse has moved while button is down
var delta = getWorldPoint(Input.mousePosition) - MouseDownPosition;
// Adjust camera by distance moved, so mouse/finger stays at exact location (in world, since we are using getWorldPoint for everything).
Camera.main.transform.Translate(delta.x, delta.z, 0);
}
}
// This works by casting a ray. For this to work well, this ray should always hit your "ground". Setup ignore layers if you need to ignore other colliders.
// Only tested this with a simple box collider as ground (just one flat ground).
private Vector3 getWorldPoint(Vector2 screenPoint)
{
RaycastHit hit;
Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
return hit.point;
}
}