Unity: Adding impulse to a ball from a certain position - unity3d

I'm making a 3d pool game and I stuck with this problem.
I can't find the needed function to add an impulse to a ball which will make the ball to spin.
In other words, how to set the AIM point from where I want to add the impulse?
If I use AddForce or AddTorque, it seems that it calculates it from the Center of the Ball.
But how to specify the aim point (Left english, Right english, etc...)?
And how to route the ball to the camera direction after a cue hit the ball?

At first look I think you should check Rigidbody.AddForceAtPosition. You should calculate the aim points somehow in world coordinates also.
I am relatively new on Unity but the main idea came to my mind first rotate the cue vector by y axis by a small amount angle, then calculate the hit point for each regions by casting physics ray then finally apply impulse by using Rigidbody.AddForceAtPosition. I tried to write a sample code:
public class SphereController : MonoBehaviour {
private Vector3 cueStartPoint;
void Start () {
cueStartPoint = new Vector3(0, 0.5f, -13f);
}
void Update () {
//direction from cue to center
var direction = transform.position - cueStartPoint;
//rotate cue to aim right english
var angleForRightEnglish = 5f;
var directionForRight = Quaternion.AngleAxis(angleForRightEnglish, Vector3.up) * direction;
Debug.DrawRay(cueStartPoint, directionForRight, Color.red, 10f);
//rotate cue to aim right english
var angleForLeftEnglish = -5f;
var directionForLeft = Quaternion.AngleAxis(angleForLeftEnglish, Vector3.up) * direction;
Debug.DrawRay(cueStartPoint, directionForLeft, Color.blue, 10f);
//try a sample hit from right
if (Input.GetKeyDown(KeyCode.RightArrow)) {
var forceMagnitude = 1f;
var hitForce = forceMagnitude * directionForRight.normalized;
//calculate right hit point on sphere surface
RaycastHit hitInfo;
if (Physics.Raycast(cueStartPoint, directionForRight, out hitInfo)) {
var rightHitPoint = hitInfo.point;
gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hitForce, rightHitPoint, ForceMode.Impulse);
}
}
//try a sample hit from left
if (Input.GetKeyDown(KeyCode.LeftArrow)) {
var forceMagnitude = 1f;
var hitForce = forceMagnitude * directionForLeft.normalized;
//calculate left hit point on sphere surface
RaycastHit hitInfo;
if (Physics.Raycast(cueStartPoint, directionForLeft, out hitInfo)) {
var leftHitPoint = hitInfo.point;
gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hitForce, leftHitPoint, ForceMode.Impulse);
}
}
}
}
this script should be added to ball as component of course, hope this helps.

Related

Using Input.Gyro to get the amount of "tilt" from an origin rotation

In my scenario, I have a table (plane) that a ball will roll around on using nothing but physics giving the illusion that the mobile device is the table using Input.gyro.attitude. Taking it one step further, I would like this relative to the device origin at the time Start() is called, so if it is not being held in front of a face or flat on the table, but just relative to where it started, and may even be reset when the ball is reset. So the question is, is how do I get the difference between the current attitude and the origin attitude, then convert the X and Z(?) difference into a Vector3 to AddForce() to my ball object whilst capping the max rotation at about 30 degrees? I've looked into a lot of Gyro based input manager scripts and nothing really helps me understand the mystery of Quaternions.
I could use the relative rotation to rotate the table itself, but then I am dealing with the problem of rotating the camera along the same rotation, but also following the ball at a relative height but now with a tilted offset.
AddForce() works well for my purposes with Input.GetAxis in the Editor, just trying to transition it to the device without using a Joystick style UI controller.
Edit: The following code is working, but I don't have the right angles/euler to give the right direction. The game is played in Landscape Left/Right only, so I should only need a pitch and yaw axis (imagine the phone flat on a table), but not roll (rotated around the camera/screen). I may eventually answer my own question through trial and error, which I am sure is what most programmers do.... XD
Started on the right track through this answer:
Answer 434096
private Gyroscope m_Gyro;
private speedForce = 3.0f;
private Rigidbody rb;
private void Start() {
m_Gyro = Input.gyro;
m_Gyro.enabled = true;
rb = GetComponent<Rigidbody>();
}
private Vector3 GetGyroForces() {
Vector3 resultantForce = Vector3.zero;
//Quaternion deviceRotation = new Quaternion(0.5f, 0.5f, -0.5f, -0.5f) * m_Gyro.attitude * new Quaternion(0,1,0,0);
float xForce = GetAngleByDeviceAxis(Vector3.up);
float zForce = GetAngleByDeviceAxis(Vector3.right);
//float zForce = diffRot.z;
resultantForce = new Vector3(xForce, 0, zForce);
return resultantForce;
}
private float GetAngleByDeviceAxis(Vector3 axis) {
Quaternion currentRotation = m_Gyro.attitude;
Quaternion eliminationOfOthers = Quaternion.Inverse(Quaternion.FromToRotation(axis, currentRotation * axis));
Vector3 filteredEuler = (eliminationOfOthers * currentRotation).eulerAngles;
float result = filteredEuler.z;
if (axis == Vector3.up) {
result = filteredEuler.y;
}
if (axis == Vector3.right) {
result = (filteredEuler.y > 90 && filteredEuler.y < 270) ? 180 - filteredEuler.x : filteredEuler.x;
}
return result;
}
void FixedUpdate() {
#if UNITY_EDITOR
rb.AddForce(new Vector3(Input.GetHorizontal * speedForce, 0, Input.GetVertical * speedForce));
#endif
#if UNITY_ANDROID
rb.AddForce(GetGyroForces);
#endif
}

How to make a player only move in 1 direction (Unity)

I am trying to create a game in Unity where the player can only move in the direction it is facing, but the following code allows the player to move in all 4 directions.
(This is for a 3D project)
Any help would be appreciated! Thanks!
public class PlayerController : MonoBehaviour {
public float speed;
private Rigidbody rb;
void Start() {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
}
So I didn't get what you wanted: first you said you wanted it to move only forward, then the only thing you have to do is get when he pressed a key and move forward while is not unpressed.
If you wanted to say that it can move all directions BUT only one at a time, the you will have to put the same code but with some changes:
First of all to make it move forward you have to get the forward of the transform, otherwise it will move in the same direction if you rotate it (you don't want that, no?).
Vector3 moveDirection = (transform.forward * Input.GetAxis("Vertical") + transform.right * Input.GetAxis("Horizontal")).normalized;
moveDirection.y = 0;
rb.velocity = moveDirection;
Then, to make that it ONLY moves to one direction at a time, you have to put the priority of the greatest axis number and if it's equal then you should think if you want to move forward or right (with it's axis value).
From the code you posted, I'm not sure where you are storing the player's facing-direction. However, I presume that it is stored as a Quaternion. If you have a player rotation quaternion called playerRotation, then you could do this (warning - untested):
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 normal = playerRotation * Vector3.forward;
Vector3 movement = Vector3.Dot(normal, input) * input;
If the game is first-person, then you can take a shortcut and just use Camera.current.transform.forward instead of the normal vector.
This will project the input direction onto the normal with the player's facing direction so that your movement force can only be in that direction.

Enemy Ai unityscript movement issue

The code I am using currently makes the enemy notice me at a distance then follow me if I get closer. The issue I am having is with how they move. I am building a Minecraft style game but I cant get the enemies to stay on the ground and jump up each block like I have too with the fps controller. They just seem to float the shortest distance possible towards me.
Code:
var target : Transform; //the enemy's target
var moveSpeed = 3; //move speed
var rotationSpeed = 3; //speed of turning
var range : float=10f;
var range2 : float=10f;
var stop : float=0;
var myTransform : Transform; //current transform data of this enemy
function Awake()
{
myTransform = transform; //cache transform data for easy access/preformance
}
function Start()
{
target = GameObject.FindWithTag("1Player").transform; //target the player
}
function Update () {
//rotate to look at the player
var distance = Vector3.Distance(myTransform.position, target.position);
if (distance<=range2 && distance>=range){
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
}
else if(distance<=range && distance>stop){
//move towards the player
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
}
else if (distance<=stop) {
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
}
}
Okay now here is what you are missing:
1.Lock the rotation on x and z axis to zero
myTransform.eulerAngles = new Vector3(0f, myTransform.eulerAngles.y, 0);
Just add this line at the end.
2.Make a short distance raycast in front of the AI and if it detects some obstacle, stop the current movement and move by y axis, if there is no obstacle move like you wrote in your script.
As for the raycasting goes, there is no need for me to write it here, you can find more info about it in Unity3D documentation here:
http://docs.unity3d.com/ScriptReference/Physics.Raycast.html and this tutorial is good for staters: https://unity3d.com/learn/tutorials/modules/beginner/physics/raycasting

Unity - Get Local Mouse Position from the center of the Gameobject clicked on

I want to get the mouse clicked position from the center of the gameobject, say a Sphere of Scale(1, 1, 1), for instance. If I click on the center of the sphere it should return the x component as zero, on clicking to the extreme left of the sphere, it should return -0.5 as the x component of the vector3 and 0.5 on clicking the extreme right of the sphere. The below code helps me achieve this when at origin. However, there is one constraint for it. The Sphere has to be positioned at (0, anything, anything) (as I am concerned with the x axis).
Any help on how can I achieve this regardless of the Sphere position?
bool isGameOver = false;
float pointX;
// Update is called once per frame
void Update () {
if(!isGameOver){
RaycastHit hit;
if(Input.GetMouseButtonDown(0)){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out hit)){
if(hit.transform.tag =="Ball"){
pointX = transform.InverseTransformPoint(hit.point).x;
}
}
}
}
}
Are you using a perspective camera or ortographic?
If you are using perspective and you move your game object to right, you cant hit on the 0,5 of the right side. Because is behide your visible part of ball.
Only can do it with orto camera
Your code is ok for this.
If you need the object position only need to add the transform.localposition to your pointX.
pointX = transform.localPosition.x + transform.InverseTransformPoint(hit.point).x;
Use this code to rotate your gameobject looking for the camera and you get now the right x coordinate you need.
void Update () {
if(!isGameOver)
{
Vector3 offset = transform.position - Camera.main.transform.position;
transform.LookAt(transform.position + offset);
RaycastHit hit;
if(Input.GetMouseButtonUp(0)){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out hit)){
if(hit.transform.tag =="Ball")
{
pointX = hit.transform.InverseTransformPoint(hit.point);
Debug.Log ("X:" + pointX.ToString());
}
}
}
}
}

Unity - Limit Camera Movement XY axis

I have the following code which works really well for scrolling map using the draggable mouse. I am trying to define limits so that I cannot scroll too far in either the x or y co-ordinates. I've seen various examples of code, such as:
"transform.position.x = Mathf.Clamp(-100, 100);" though have not been able to incorporate it into the code. I am a bit of a beginner to all this and have just done a 2D tutorial animating zombies and have a camera that can scroll, but would like to add limits to how far it can scroll in any given directions.
Thanks heaps
Adam
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;
}
}
What exactly is the error you are getting? I imagine it is something along the lines of being unable to modify the return value of position because it is not a variable.
If this is the case, you should however be able to set the whole position vector at once. So try something along the lines of this:
var pos = transform.position;
transform.position = new Vector3(
Math.clampf(pos.x, -100, 100),
Math.clampf(pos.y, -100, 100),
pos.z
);
You may need to swap around clamping Y and Z, depending on how you are using them.