Unity 3D Spring joint textures - c#

Hello i'm doing a simple 3d game and wanted to make something like a grappling hook and it all works perfectly, but i can't find a solution how to make it to have a texture. Here's my code:
SpringJoint joint;
public GameObject playerCam;
Vector3 grapplepoint;
private void StartGrapplinghook()
{
print("grapple");
RaycastHit[] hits = Physics.RaycastAll(playerCam.transform.position, playerCam.transform.forward, 30f);
if (hits.Length < 1) return;
grapplepoint = hits[0].point;
joint = gameObject.AddComponent<SpringJoint>();
joint.autoConfigureConnectedAnchor = false;
joint.connectedAnchor = grapplepoint;
joint.spring = 2.5f;
joint.damper = 0.25f;
}
Hope you guys can help me.

I would use a LineRenderer for this, and update its positions every frame if it's enabled.
Make and configure a LineRenderer component on the same gameobject the script is on. Explanation for code in comments.
[RequireComponent(typeof(LineRenderer))]
public class MyClassName : MonoBehaviour
{
[SerializeField]
LineRenderer grappleRenderer;
SpringJoint joint;
public GameObject playerCam;
Vector3 grapplepoint;
void Awake()
{
// Whatever is already here...
// If you don't need a fresh grapple copy every time,
// create them once here or in the editor and re-use them
joint = gameObject.AddComponent<SpringJoint>();
joint.autoConfigureConnectedAnchor = false;
joint.spring = 2.5f;
joint.damper = 0.25f;
grappleRenderer = GetComponent<LineRenderer>();
grappleRenderer.positionCount = 2; // this could just be set in the inspector
// Don't use the grapple by default
joint.enabled = false;
grappleRenderer.enabled = false;
}
private void StartGrapplinghook()
{
print("grapple");
RaycastHit[] hits = Physics.RaycastAll(playerCam.transform.position,
playerCam.transform.forward, 30f);
if (hits.Length < 1) return;
grapplepoint = hits[0].point;
joint.enabled = true;
joint.connectedAnchor = grapplepoint;
grappleRenderer.enabled = true;
}
void StopGrapplingHook()
{
// turn off joint and renderer
joint.enabled = false;
grappleRenderer.enabled = false;
}
// grapple rendering can happen after all other updates finish
// determining if it should be rendered or not that frame
void LateUpdate()
{
if (grappleRenderer.enabled)
{
Vector3 grappleStart = transform.position; // or something that makes sense
grappleRenderer.SetPoints(new Vector3[]{grapplepoint, grappleStart});
}
}
}

Related

Unity - How to Anchor to an Object if it Collides With a Child Object

I have a telescoping spike gadget in a game that I'm working on. The spike is supposed to be an arm attachment for the player which will allow them to stick to a wall once the spike tip (the deepest child object) touches something. Essentially I want it to "stab" into the wall and hold the player in place.
The closest I've gotten to this has been by setting "Contact Pairs Mode" to "Enable Kinematic Kinematic Pairs", then adding a kinematic RigidBody and a FixedJoint to the tip. Here is the code for the spike itself:
private IEnumerator _ExpandSequence()
{
mExpanding = true;
if (mCollapsing)
{
yield return new WaitUntil(() => !mCollapsing);
}
mBaseGoal = transform.localPosition + mBaseDistance * Vector3.up;
mConeGoal = mSpikeSections[5].localPosition + mConeDistance * Vector3.up;
while (transform.localPosition.y > mBaseSpikeEnd || mSpikeSections[1].localPosition.y > mCylinderEnd
|| mSpikeSections[5].localPosition.y > mConeEnd)
{ //only using the first cylinder section in the condition since they all move the same distance
transform.localPosition = Vector3.MoveTowards(transform.localPosition, mBaseGoal, mSpeed);
mSpikeSections[1].localPosition = mSpikeSections[2].localPosition = mSpikeSections[3].localPosition =
mSpikeSections[4].localPosition = Vector3.MoveTowards(mSpikeSections[1].localPosition, Vector3.up * mCylinderDistance, mSpeed);
mSpikeSections[5].localPosition = Vector3.MoveTowards(mSpikeSections[5].localPosition, mConeGoal, mSpeed);
if (Mathf.Approximately(transform.localPosition.y, mBaseSpikeEnd) &&
Mathf.Approximately(mSpikeSections[1].localPosition.y, mCylinderEnd) &&
Mathf.Approximately(mSpikeSections[5].localPosition.y, mConeEnd))
{
transform.localPosition = mBaseGoal;
mSpikeSections[5].localPosition = mConeGoal;
break;
}
yield return null;
}
mExpanding = false;
mExtended = true;
}
Here is the code for the spike tip's collision:
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
//Anchor
mFj.connectedBody = col.rigidbody;
}
else
{
//TODO
}
}
And here is the hierarchy for the player's assets:
Currently when the spike collides with a surface, I can see in the editor that a connected body is registered
Despite this, the player bounces off and is able to move freely as if the joint didn't exist. Can anyone help me find out why the player isn't being fixed to the wall?
TL;DR: How can I get a child object with a RigidBody to connect to another object via a FixedJoint?
First, create two FixedJoint components on the tip and assign them to fields in the script. Assign the rigidbody of the player to a field in the script. Assign the rigidbody and collider of the tip to a field in the script.
Second, when the collision occurs, turn isKinematic off, so that the tip affects and is affected by joints (e.g., so it follows anchoredenemies if they move)
Third, when you want to end the anchor, you should just be able to set isKinematic to true again, assign both of its joint connected bodies to null, and if necessary disable its collider to avoid
[SerializeField] FixedJoint mFixedJointTarget; // 1st fixed joint on tip
[SerializeField] FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
[SerializeField] Rigidbody myRB; // rigidbody of tip
[SerializeField] Rigidbody playerRB; // player's rigidbody
[SerializeField] Collider myColl; // if necessary to avoid re-attachment
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
myRB.isKinematic = false;
mFixedJointTarget.connectedBody = targetRB;
mFixedJointPlayer.connectedBody = playerRB;
}
void StopAnchor()
{
myRB.isKinematic = true;
mFixedJointTarget.connectedBody = null;
mFixedJointPlayer.connectedBody = null;
// re-enable before shooting again, of course
// may not be necessary depending on what else you do when you stop anchoring.
myColl.enabled = false;
}
You can do the same with the tip never being kinematic if you would like, you will just need to Destroy the joints instead of setting the connectedBodys to null, and also create the joints only when a collision occurs, this might look like the following:
//on drill with useGravity = false
public class DrillShot : MonoBehaviour
{
FixedJoint mFixedJointTarget; // 1st fixed joint on tip
FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
Rigidbody rb;
[SerializeField] Rigidbody playerRB; // player's rigidbody
Renderer rend;
[SerializeField] float speed = 10f;
void Start()
{
rb = GetComponent<Rigidbody>();
rend = GetComponent<Renderer>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
mFixedJointPlayer = gameObject.AddComponent<FixedJoint>();
mFixedJointPlayer.connectedBody = playerRB;
mFixedJointTarget = gameObject.AddComponent<FixedJoint>();
mFixedJointTarget.connectedBody = targetRB;
}
void StopAnchor()
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
rend.enabled = false;
}
void Shoot()
{
rend.enabled = true;
transform.position = playerRB.position + Vector3.right;
rb.velocity = Vector3.right * speed;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Shoot();
}
else if (Input.GetKeyDown(KeyCode.R))
{
StopAnchor();
}
}
}
// on wall, with rigidbody tagged "Environment",
// xyz freeze rotation mass 100, use gravity = false
public class MoveRandomly : MonoBehaviour
{
Vector3 curTarget;
Vector3 startPos;
[SerializeField] float switchTargetPeriod = 2f;
[SerializeField] float targetRadius = 1f;
[SerializeField] float dampener = 0.05f;
Rigidbody rb;
float t = 0f;
void Start()
{
rb = GetComponent<Rigidbody>();
startPos = transform.position;
}
void FixedUpdate()
{
t -= Time.deltaTime;
if (t < 0)
{
t = switchTargetPeriod;
curTarget = startPos + Random.insideUnitSphere * targetRadius;
}
Vector3 diff = curTarget - transform.position;
rb.AddForce(diff * dampener, ForceMode.VelocityChange);
}
}

Multiple clones of model spawning in target (Vuforia and Unity)

I am trying to place a sphere on a ground plane and on a button click remove the sphere and place a cube.
This is the script attached to the AR camera object.
using UnityEngine;
using Vuforia;
using System.Collections;
public class ModelSwapper : MonoBehaviour {
public AnchorStageBehaviour theTrackable;
public Transform myModelPrefab;
private bool mSwapModel = false;
// Use this for initialization
void Start () {
Transform myModelTrf = GameObject.Instantiate(myModelPrefab) as Transform;
myModelTrf.parent = theTrackable.transform;
myModelTrf.localPosition = new Vector3(0f, 0f, 0f);
myModelTrf.localRotation = Quaternion.identity;
myModelTrf.localScale = new Vector3(0.05f, 0.05f, 0.05f);
myModelTrf.gameObject.active = true;
}
// Update is called once per frame
void Update () {
if (mSwapModel && theTrackable != null) {
SwapModel();
mSwapModel = false;
}
}
void OnGUI() {
if (GUI.Button (new Rect(50,50,120,40), "Swap Model")) {
mSwapModel = true;
}
}
private void SwapModel() {
GameObject trackableGameObject = theTrackable.gameObject;
//disable any pre-existing augmentation
for (int i = 0; i < trackableGameObject.transform.GetChildCount(); i++)
{
Transform child = trackableGameObject.transform.GetChild(i);
child.gameObject.active = false;
}
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// Re-parent the cube as child of the trackable gameObject
cube.transform.parent = theTrackable.transform;
// Adjust the position and scale
// so that it fits nicely on the target
cube.transform.localPosition = new Vector3(0, 0.2f, 0);
cube.transform.localRotation = Quaternion.identity;
cube.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
// Make sure it is active
cube.active = true;
}
}
But instead, it spawns multiple cubes along with the sphere.
Multiple clones
Since, there isn't much documentation or anything else on Vuforia ground plane, it's hard to get things done. Why is this behaving strange?
You could avoid the Update and it should fix your issue, replace these two methods in your script with the following:
void Update () {
}
void OnGUI() {
if (GUI.Button (new Rect(50,50,120,40), "Swap Model")) {
if(theTrackable)
{
SwapModel();
}
}
}

Line renderer not working as expected?

I am using line renderer to create fruit ninja style swipe effect. But it is not working.Line was supposed to follow the mouse pos but it is not doing so Here is the code. Please help me to solve this. And this script is attached to gameobject line(empty) at position of (0,0,1).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineFOllow : MonoBehaviour {
int vertexcount =0;
bool mousedown = false;
private LineRenderer line;
void Start () {
line = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0)) // gets called when mouse is clicked
{
mousedown = true;
}
if (mousedown)
{
Debug.Log("called");
line.positionCount = vertexcount+1;
Vector3 mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
line.SetPosition(vertexcount, mousepos);
vertexcount++;
}
if (Input.GetMouseButtonUp(0))// gets called when mouse is released
{
vertexcount = 0;
line.positionCount = 0;
mousedown = false;
}
}
}
Enable worldspace coordinates for linerender.
In your case I think is better to use a TrailRenderer.
private TrailRenderer trail;
public float depth;
void Start()
{
depth = 10;
trail = GetComponent<TrailRenderer>();
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector3 mousepos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, depth);
Vector3 position = Camera.main.ScreenToWorldPoint(mousepos);
trail.transform.position = position;
}
}
Change the Time, MinVertexDistance (and other) properties on your TrailRenderer component to get the desired effect.

Unity Coroutine for gameMenu

I'm building a game like the roll a ball game on the tutorials in the Unity website, now I'm focused on the gameover menu, when the player lose all his lives I activate the gameover text, the yes and no text and the rawImage that is displayed behind the yes text by default, the thing is I want the image that is behind the yes text to go down to no and up to yes with the arrow keys. I think I implemented it right with the keys, but I feel like I need a coroutine to build this, because it just leaves the gameover function, without waiting the user input, so after this how can I build this in a right mode?
I did this at the moment:
public class Manager : MonoBehaviour {
private float checkPoint = 0;
public GameObject Ball;
private GameObject cam;
private Object newBall;
public int lifes;
public Text Lifetext;
private bool gameIsOver = false;
private Text gameOver;
private Text playAgain;
private Text yes;
private Text no;
private RawImage TopMenuBall;
void Awake(){
Lifetext = GameObject.Find("Lifes").GetComponent<Text>();
gameOver = GameObject.Find("GameOver").GetComponent<Text>();
playAgain = GameObject.Find("PlayAgain").GetComponent<Text>();
yes = GameObject.Find("Yes").GetComponent<Text>();
no = GameObject.Find("No").GetComponent<Text>();
TopMenuBall = GameObject.Find("TopMenuBall").GetComponent<RawImage>();
gameOver.enabled = false;
playAgain.enabled = false;
yes.enabled = false;
no.enabled = false;
TopMenuBall.enabled = false;
TopMenuBall.transform.localPosition = new Vector3(-68,-41,0);
}
void Start(){
lifes = 3;
Lifetext.text = " x" + lifes;
SpawnBall ();
cam = GameObject.Find ("Main Camera");
}
public void LifeUp(){
lifes++;
Lifetext.text = "X " + lifes;
}
public void LifeDown(){
if (lifes <= 0) {
GameOver ();
} else {
lifes--;
Lifetext.text = "X " + lifes;
}
}
public void GameOver(){
Debug.Log ("gameover");
gameOver.enabled = true;
playAgain.enabled = true;
yes.enabled = true;
no.enabled = true;
TopMenuBall.enabled = true;
if (Input.GetKeyDown (KeyCode.DownArrow)) {
TopMenuBall.transform.localPosition = new Vector3 (-68, -82, 0);
Debug.Log ("up");
}
else if (Input.GetKeyDown(KeyCode.UpArrow))
TopMenuBall.transform.localPosition = new Vector3(-68,-41,0);
}
void SpawnBall(){
Vector3 spawnPosition = new Vector3 (0.02f,1.4f,-39f);
Quaternion spawnRotation = Quaternion.identity;
newBall = Instantiate (Ball, spawnPosition, spawnRotation);
Camera.main.GetComponent<cameraMove>().player = (GameObject)newBall;
}
when the user lose all the lifes it enters the gameover function and there is the problem, how can I solve this?
You should use Update() method to get the user inputs. Because you need it to be constantly checked and the Update method exists exactly for this kind of situation, once it is called every frame.
So, your code should look like this:
void Update() {
if (TopMenuBall.enabled) {
if (Input.GetKeyDown (KeyCode.DownArrow)) {
TopMenuBall.transform.localPosition = new Vector3 (-68, -82, 0);
Debug.Log ("up");
}
else if (Input.GetKeyDown(KeyCode.UpArrow))
TopMenuBall.transform.localPosition = new Vector3(-68,-41,0);
}
}

How can I associate the First Person Controller in Unity with the VROneSDK and VROneSDKHead?

I would like to combine the First Person Controller from the Unity standard assets with the camera from the VROneSDK and VROneSDKHead, where the rotation of the character is controlled by the head tracking from the VROneSDK.
How can I do this with the SDK?
Unity's FPS controller uses the script MouseLook.cs to control the camera. The script is attached twice in this perfab. At the root object the script controls horizontal rotation and at the camera the script controls the vertical rotation.
Remove both scripts from the FPS controller.
Disable the main camera in the FPS controller. Attach the VROneSDK prefab to the FPS controller (as child of FPS controller) and copy the position and rotation values from the previous camera.
Create a new C# script VROneHeadLook.cs with the following content:
using UnityEngine;
using System.Collections;
public class VROneHeadLook : MonoBehaviour {
public bool useAngleX = true;
public bool useAngleY = true;
public bool useAngleZ = true;
public bool useRealHorizontalAngle = false;
public bool resetViewOnTouch = true;
private Quaternion initialRotation = Quaternion.identity;
private Quaternion currentRotation;
private static Vector3 gyroAngles; // original angles from gyro
private static Vector3 usedAngles; // converted into unity world coordinates
private int userSleepTimeOut; // original device SleepTimeOut setting
private bool gyroAvail = false;
// Use this for initialization
void Start () {
Input.compensateSensors = true;
}
// Update is called once per frame
void FixedUpdate () {
if (gyroAvail == false) {
if (Input.gyro.attitude.eulerAngles != Vector3.zero && Time.frameCount > 30) {
gyroAvail = true;
initialRotation = Input.gyro.attitude;
}
return; // early out
}
// reset origin on touch or not yet set origin
if(resetViewOnTouch && (Input.touchCount > 0))
initialRotation = Input.gyro.attitude;
// new rotation
currentRotation = Quaternion.Inverse(initialRotation)*Input.gyro.attitude;
gyroAngles = currentRotation.eulerAngles;
//usedAngles = Quaternion.Inverse (currentRotation).eulerAngles;
usedAngles = gyroAngles;
// reset single angle values
if (useAngleX == false)
usedAngles.x = 0f;
if (useAngleY == false)
usedAngles.y = 0f;
if (useAngleZ == false)
usedAngles.z = 0f;
if (useRealHorizontalAngle)
usedAngles.y *= -1;
transform.localRotation = Quaternion.Euler (new Vector3(-usedAngles.x, usedAngles.y, usedAngles.z));
}
void OnEnable() {
// sensor on
Input.gyro.enabled = true;
initialRotation = Quaternion.identity;
gyroAvail = false;
// store device sleep timeout setting
userSleepTimeOut = Screen.sleepTimeout;
// disable sleep timeout when app is running
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void OnDisable() {
// restore original sleep timeout
Screen.sleepTimeout = userSleepTimeOut;
//sensor off
Input.gyro.enabled = false;
}
}
Attach this script to the FPS controller and the VROneSDK prefab. For the FPS controller set all script variables to false except 'Use Angle Y' which should be true.
For the script attached to the VROneSDK prefab set all script variables to false except 'Use Angle X'

Categories