重新导入obi
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class CharacterControl2D : MonoBehaviour {
|
||||
|
||||
public float acceleration = 10;
|
||||
public float maxSpeed = 8;
|
||||
public float jumpPower = 2;
|
||||
|
||||
private Rigidbody unityRigidbody;
|
||||
|
||||
public void Awake(){
|
||||
unityRigidbody = GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
|
||||
void FixedUpdate () {
|
||||
|
||||
unityRigidbody.AddForce(new Vector3(Input.GetAxis("Horizontal")*acceleration,0,0));
|
||||
|
||||
unityRigidbody.linearVelocity = Vector3.ClampMagnitude(unityRigidbody.linearVelocity,maxSpeed);
|
||||
|
||||
if (Input.GetButtonDown("Jump")){
|
||||
unityRigidbody.AddForce(Vector3.up * jumpPower,ForceMode.VelocityChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82b0f164ac14247fd8a566109fbbe771
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
public class CraneController : MonoBehaviour {
|
||||
|
||||
ObiRopeCursor cursor;
|
||||
ObiRope rope;
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
cursor = GetComponentInChildren<ObiRopeCursor>();
|
||||
rope = cursor.GetComponent<ObiRope>();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
if (Input.GetKey(KeyCode.W)){
|
||||
if (rope.restLength > 6.5f)
|
||||
cursor.ChangeLength(rope.restLength - 1f * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.S)){
|
||||
cursor.ChangeLength(rope.restLength + 1f * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.A)){
|
||||
transform.Rotate(0,Time.deltaTime*15f,0);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.D)){
|
||||
transform.Rotate(0,-Time.deltaTime*15f,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5551d37f45fd044ff8768019501c20a5
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
[RequireComponent(typeof(ObiRope))]
|
||||
public class CursorController : MonoBehaviour
|
||||
{
|
||||
|
||||
ObiRopeCursor cursor;
|
||||
ObiRope rope;
|
||||
public float minLength = 0.1f;
|
||||
public float speed = 1;
|
||||
|
||||
// Use this for initialization
|
||||
void Start ()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
cursor = GetComponent<ObiRopeCursor>();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update ()
|
||||
{
|
||||
if (Input.GetKey(KeyCode.W) && cursor != null)
|
||||
{
|
||||
if (rope.restLength > minLength)
|
||||
cursor.ChangeLength(rope.restLength - speed * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.S) && cursor != null)
|
||||
{
|
||||
cursor.ChangeLength(rope.restLength + speed * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.A)){
|
||||
rope.transform.Translate(Vector3.left * Time.deltaTime,Space.World);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.D)){
|
||||
rope.transform.Translate(Vector3.right * Time.deltaTime,Space.World);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83c7e82b3aac04e408bc8bfe9027c983
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,213 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
/**
|
||||
* Sample component that shows how to use Obi Rope to create a grappling hook for a 2.5D game.
|
||||
* 95% of the code is the grappling hook logic (user input, scene raycasting, launching, attaching the hook, etc) and parameter setup,
|
||||
* to show how to use Obi completely at runtime. This might not be practical for real-world scenarios,
|
||||
* but illustrates how to do it.
|
||||
*
|
||||
* Note that the choice of using actual rope simulation for grapple dynamics is debatable. Usually
|
||||
* a simple spring works better both in terms of performance and controllability.
|
||||
*
|
||||
* If complex interaction is required with the scene, a purely geometry-based approach (ala Worms ninja rope) can
|
||||
* be the right choice under certain circumstances.
|
||||
*/
|
||||
public class ExtendableGrapplingHook : MonoBehaviour
|
||||
{
|
||||
|
||||
public ObiSolver solver;
|
||||
public ObiCollider character;
|
||||
|
||||
public Material material;
|
||||
public ObiRopeSection section;
|
||||
|
||||
[Range(0, 1)]
|
||||
public float hookResolution = 0.5f;
|
||||
public float hookExtendRetractSpeed = 2;
|
||||
public float hookShootSpeed = 30;
|
||||
public int particlePoolSize = 100;
|
||||
|
||||
private ObiRope rope;
|
||||
private ObiRopeBlueprint blueprint;
|
||||
private ObiRopeExtrudedRenderer ropeRenderer;
|
||||
|
||||
private ObiRopeCursor cursor;
|
||||
|
||||
private RaycastHit hookAttachment;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
|
||||
// Create both the rope and the solver:
|
||||
rope = gameObject.AddComponent<ObiRope>();
|
||||
ropeRenderer = gameObject.AddComponent<ObiRopeExtrudedRenderer>();
|
||||
ropeRenderer.section = section;
|
||||
ropeRenderer.uvScale = new Vector2(1, 4);
|
||||
ropeRenderer.normalizeV = false;
|
||||
ropeRenderer.uvAnchor = 1;
|
||||
rope.GetComponent<MeshRenderer>().material = material;
|
||||
|
||||
// Setup a blueprint for the rope:
|
||||
blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
|
||||
blueprint.resolution = 0.5f;
|
||||
blueprint.pooledParticles = particlePoolSize;
|
||||
|
||||
// Tweak rope parameters:
|
||||
rope.maxBending = 0.02f;
|
||||
|
||||
// Add a cursor to be able to change rope length:
|
||||
cursor = rope.gameObject.AddComponent<ObiRopeCursor>();
|
||||
cursor.cursorMu = 0;
|
||||
cursor.direction = true;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyImmediate(blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raycast against the scene to see if we can attach the hook to something.
|
||||
*/
|
||||
private void LaunchHook()
|
||||
{
|
||||
|
||||
// Get the mouse position in the scene, in the same XY plane as this object:
|
||||
Vector3 mouse = Input.mousePosition;
|
||||
mouse.z = transform.position.z - Camera.main.transform.position.z;
|
||||
Vector3 mouseInScene = Camera.main.ScreenToWorldPoint(mouse);
|
||||
|
||||
// Get a ray from the character to the mouse:
|
||||
Ray ray = new Ray(transform.position, mouseInScene - transform.position);
|
||||
|
||||
// Raycast to see what we hit:
|
||||
if (Physics.Raycast(ray, out hookAttachment))
|
||||
{
|
||||
// We actually hit something, so attach the hook!
|
||||
StartCoroutine(AttachHook());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IEnumerator AttachHook()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
// Clear pin constraints:
|
||||
var pinConstraints = rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
|
||||
pinConstraints.Clear();
|
||||
|
||||
Vector3 localHit = rope.transform.InverseTransformPoint(hookAttachment.point);
|
||||
|
||||
// Procedurally generate the rope path (just a short segment, as we will extend it over time):
|
||||
int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);
|
||||
blueprint.path.Clear();
|
||||
blueprint.path.AddControlPoint(Vector3.zero, Vector3.zero, Vector3.zero, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "Hook start");
|
||||
blueprint.path.AddControlPoint(localHit.normalized * 0.5f, Vector3.zero, Vector3.zero, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "Hook end");
|
||||
blueprint.path.FlushEvents();
|
||||
|
||||
// Generate the particle representation of the rope (wait until it has finished):
|
||||
yield return blueprint.Generate();
|
||||
|
||||
// Set the blueprint (this adds particles/constraints to the solver and starts simulating them).
|
||||
rope.ropeBlueprint = blueprint;
|
||||
|
||||
// wait one frame:
|
||||
yield return null;
|
||||
|
||||
rope.GetComponent<MeshRenderer>().enabled = true;
|
||||
|
||||
// set masses to zero, as we're going to override positions while we extend the rope:
|
||||
for (int i = 0; i < rope.activeParticleCount; ++i)
|
||||
solver.invMasses[rope.solverIndices[i]] = 0;
|
||||
|
||||
float currentLength = 0;
|
||||
|
||||
// while the last particle hasn't reached the hit, extend the rope:
|
||||
while (true)
|
||||
{
|
||||
// calculate rope origin in solver space:
|
||||
Vector3 origin = solver.transform.InverseTransformPoint(rope.transform.position);
|
||||
|
||||
// update direction and distance to hook point:
|
||||
Vector3 direction = hookAttachment.point - origin;
|
||||
float distance = direction.magnitude;
|
||||
direction.Normalize();
|
||||
|
||||
// increase length:
|
||||
currentLength += hookShootSpeed * Time.deltaTime;
|
||||
|
||||
// if we have reached the desired length, break the loop:
|
||||
if (currentLength >= distance)
|
||||
{
|
||||
cursor.ChangeLength(distance);
|
||||
break;
|
||||
}
|
||||
|
||||
// change rope length (clamp to distance between rope origin and hook to avoid overshoot)
|
||||
cursor.ChangeLength(Mathf.Min(distance, currentLength));
|
||||
|
||||
// iterate over all particles in sequential order, placing them in a straight line while
|
||||
// respecting element length:
|
||||
float length = 0;
|
||||
for (int i = 0; i < rope.elements.Count; ++i)
|
||||
{
|
||||
solver.positions[rope.elements[i].particle1] = origin + direction * length;
|
||||
solver.positions[rope.elements[i].particle2] = origin + direction * (length + rope.elements[i].restLength);
|
||||
length += rope.elements[i].restLength;
|
||||
}
|
||||
|
||||
// wait one frame:
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// restore masses so that the simulation takes over now that the rope is in place:
|
||||
for (int i = 0; i < rope.activeParticleCount; ++i)
|
||||
solver.invMasses[rope.solverIndices[i]] = 10; // 1/0.1 = 10
|
||||
|
||||
// Pin both ends of the rope (this enables two-way interaction between character and rope):
|
||||
var batch = new ObiPinConstraintsBatch();
|
||||
batch.AddConstraint(rope.elements[0].particle1, character, transform.localPosition, Quaternion.identity, 0, 0, float.PositiveInfinity);
|
||||
batch.AddConstraint(rope.elements[rope.elements.Count-1].particle2, hookAttachment.collider.GetComponent<ObiColliderBase>(),
|
||||
hookAttachment.collider.transform.InverseTransformPoint(hookAttachment.point), Quaternion.identity, 0, 0, float.PositiveInfinity);
|
||||
batch.activeConstraintCount = 2;
|
||||
pinConstraints.AddBatch(batch);
|
||||
|
||||
rope.SetConstraintsDirty(Oni.ConstraintType.Pin);
|
||||
}
|
||||
|
||||
private void DetachHook()
|
||||
{
|
||||
// Set the rope blueprint to null (automatically removes the previous blueprint from the solver, if any).
|
||||
rope.ropeBlueprint = null;
|
||||
rope.GetComponent<MeshRenderer>().enabled = false;
|
||||
}
|
||||
|
||||
|
||||
void Update()
|
||||
{
|
||||
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
if (!rope.isLoaded)
|
||||
LaunchHook();
|
||||
else
|
||||
DetachHook();
|
||||
}
|
||||
|
||||
if (rope.isLoaded)
|
||||
{
|
||||
if (Input.GetKey(KeyCode.W))
|
||||
{
|
||||
cursor.ChangeLength(rope.restLength - hookExtendRetractSpeed * Time.deltaTime);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.S))
|
||||
{
|
||||
cursor.ChangeLength(rope.restLength + hookExtendRetractSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b77478c97cefb4dec9cc85236f7c5fca
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,152 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
/**
|
||||
* Sample component that shows how to use Obi Rope to create a grappling hook for a 2.5D game.
|
||||
* 95% of the code is the grappling hook logic (user input, scene raycasting, launching, attaching the hook, etc) and parameter setup,
|
||||
* to show how to use Obi completely at runtime. This might not be practical for real-world scenarios,
|
||||
* but illustrates how to do it.
|
||||
*
|
||||
* Note that the choice of using actual rope simulation for grapple dynamics is debatable. Usually
|
||||
* a simple spring works better both in terms of performance and controllability.
|
||||
*
|
||||
* If complex interaction is required with the scene, a purely geometry-based approach (ala Worms ninja rope) can
|
||||
* be the right choice under certain circumstances.
|
||||
*/
|
||||
public class GrapplingHook : MonoBehaviour
|
||||
{
|
||||
|
||||
public ObiSolver solver;
|
||||
public ObiCollider character;
|
||||
public float hookExtendRetractSpeed = 2;
|
||||
public Material material;
|
||||
public ObiRopeSection section;
|
||||
|
||||
private ObiRope rope;
|
||||
private ObiRopeBlueprint blueprint;
|
||||
private ObiRopeExtrudedRenderer ropeRenderer;
|
||||
|
||||
private ObiRopeCursor cursor;
|
||||
|
||||
private RaycastHit hookAttachment;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
|
||||
// Create both the rope and the solver:
|
||||
rope = gameObject.AddComponent<ObiRope>();
|
||||
ropeRenderer = gameObject.AddComponent<ObiRopeExtrudedRenderer>();
|
||||
ropeRenderer.section = section;
|
||||
ropeRenderer.uvScale = new Vector2(1, 4);
|
||||
ropeRenderer.normalizeV = false;
|
||||
ropeRenderer.uvAnchor = 1;
|
||||
rope.GetComponent<MeshRenderer>().material = material;
|
||||
|
||||
// Setup a blueprint for the rope:
|
||||
blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
|
||||
blueprint.resolution = 0.5f;
|
||||
|
||||
// Tweak rope parameters:
|
||||
rope.maxBending = 0.02f;
|
||||
|
||||
// Add a cursor to be able to change rope length:
|
||||
cursor = rope.gameObject.AddComponent<ObiRopeCursor>();
|
||||
cursor.cursorMu = 0;
|
||||
cursor.direction = true;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyImmediate(blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raycast against the scene to see if we can attach the hook to something.
|
||||
*/
|
||||
private void LaunchHook()
|
||||
{
|
||||
|
||||
// Get the mouse position in the scene, in the same XY plane as this object:
|
||||
Vector3 mouse = Input.mousePosition;
|
||||
mouse.z = transform.position.z - Camera.main.transform.position.z;
|
||||
Vector3 mouseInScene = Camera.main.ScreenToWorldPoint(mouse);
|
||||
|
||||
// Get a ray from the character to the mouse:
|
||||
Ray ray = new Ray(transform.position, mouseInScene - transform.position);
|
||||
|
||||
// Raycast to see what we hit:
|
||||
if (Physics.Raycast(ray, out hookAttachment))
|
||||
{
|
||||
// We actually hit something, so attach the hook!
|
||||
StartCoroutine(AttachHook());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IEnumerator AttachHook()
|
||||
{
|
||||
yield return 0;
|
||||
Vector3 localHit = rope.transform.InverseTransformPoint(hookAttachment.point);
|
||||
|
||||
int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything,0);
|
||||
|
||||
// Procedurally generate the rope path (a simple straight line):
|
||||
blueprint.path.Clear();
|
||||
blueprint.path.AddControlPoint(Vector3.zero, -localHit.normalized, localHit.normalized, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "Hook start");
|
||||
blueprint.path.AddControlPoint(localHit, -localHit.normalized, localHit.normalized, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "Hook end");
|
||||
blueprint.path.FlushEvents();
|
||||
|
||||
// Generate the particle representation of the rope (wait until it has finished):
|
||||
yield return blueprint.Generate();
|
||||
|
||||
// Set the blueprint (this adds particles/constraints to the solver and starts simulating them).
|
||||
rope.ropeBlueprint = blueprint;
|
||||
rope.GetComponent<MeshRenderer>().enabled = true;
|
||||
|
||||
// Pin both ends of the rope (this enables two-way interaction between character and rope):
|
||||
var pinConstraints = rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
|
||||
pinConstraints.Clear();
|
||||
var batch = new ObiPinConstraintsBatch();
|
||||
batch.AddConstraint(rope.solverIndices[0], character, transform.localPosition, Quaternion.identity, 0, 0, float.PositiveInfinity);
|
||||
batch.AddConstraint(rope.solverIndices[blueprint.activeParticleCount - 1], hookAttachment.collider.GetComponent<ObiColliderBase>(),
|
||||
hookAttachment.collider.transform.InverseTransformPoint(hookAttachment.point), Quaternion.identity, 0, 0, float.PositiveInfinity);
|
||||
batch.activeConstraintCount = 2;
|
||||
pinConstraints.AddBatch(batch);
|
||||
|
||||
rope.SetConstraintsDirty(Oni.ConstraintType.Pin);
|
||||
}
|
||||
|
||||
private void DetachHook()
|
||||
{
|
||||
// Set the rope blueprint to null (automatically removes the previous blueprint from the solver, if any).
|
||||
rope.ropeBlueprint = null;
|
||||
rope.GetComponent<MeshRenderer>().enabled = false;
|
||||
}
|
||||
|
||||
|
||||
void Update()
|
||||
{
|
||||
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
if (!rope.isLoaded)
|
||||
LaunchHook();
|
||||
else
|
||||
DetachHook();
|
||||
}
|
||||
|
||||
if (rope.isLoaded)
|
||||
{
|
||||
if (Input.GetKey(KeyCode.W))
|
||||
{
|
||||
cursor.ChangeLength(rope.restLength - hookExtendRetractSpeed * Time.deltaTime);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.S))
|
||||
{
|
||||
cursor.ChangeLength(rope.restLength + hookExtendRetractSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94f9aa141b5964f9f91dadcc1919618c
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
|
||||
[RequireComponent(typeof(ObiRope))]
|
||||
public class HosePump : MonoBehaviour {
|
||||
|
||||
[Header("Bulge controls")]
|
||||
public float pumpSpeed = 5;
|
||||
public float bulgeFrequency = 3;
|
||||
public float baseThickness = 0.04f;
|
||||
public float bulgeThickness = 0.06f;
|
||||
public Color bulgeColor = Color.cyan;
|
||||
|
||||
[Header("Flow controls")]
|
||||
public ParticleSystem waterEmitter;
|
||||
public float flowSpeedMin = 0.5f;
|
||||
public float flowSpeedMax = 7;
|
||||
public float minEmitRate = 100;
|
||||
public float maxEmitRate = 1000;
|
||||
|
||||
private ObiRope rope;
|
||||
public ObiPathSmoother smoother;
|
||||
private float time = 0;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
smoother = GetComponent<ObiPathSmoother>();
|
||||
rope.OnBeginStep += Rope_OnBeginStep;
|
||||
}
|
||||
void OnDisable()
|
||||
{
|
||||
rope.OnBeginStep -= Rope_OnBeginStep;
|
||||
}
|
||||
|
||||
private void Rope_OnBeginStep(ObiActor actor, float stepTime)
|
||||
{
|
||||
time += stepTime * pumpSpeed;
|
||||
|
||||
float distance = 0;
|
||||
float sine = 0;
|
||||
|
||||
// iterate over all particles, changing their radius and color based on a sine wave:
|
||||
// (note this would not support resizable / cuttable ropes, to add support for that use rope.elements instead)
|
||||
for (int i = 0; i < rope.solverIndices.Length; ++i)
|
||||
{
|
||||
int solverIndex = rope.solverIndices[i];
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
int previousIndex = rope.solverIndices[i - 1];
|
||||
distance += Vector3.Distance(rope.solver.positions[solverIndex],rope.solver.positions[previousIndex]);
|
||||
}
|
||||
|
||||
sine = Mathf.Max(0, Mathf.Sin(distance * bulgeFrequency - time));
|
||||
|
||||
rope.solver.principalRadii[solverIndex] = Vector3.one * Mathf.Lerp(baseThickness,bulgeThickness, sine);
|
||||
rope.solver.colors[solverIndex] = Color.Lerp(Color.white, bulgeColor, sine);
|
||||
}
|
||||
|
||||
// change particle emission rate/speed based on sine wave at the last particle:
|
||||
if (waterEmitter != null)
|
||||
{
|
||||
var main = waterEmitter.main;
|
||||
main.startSpeed = Mathf.Lerp(flowSpeedMin,flowSpeedMax,sine);
|
||||
|
||||
var emission = waterEmitter.emission;
|
||||
emission.rateOverTime = Mathf.Lerp(minEmitRate, maxEmitRate, sine);
|
||||
}
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
if (smoother != null && waterEmitter != null)
|
||||
{
|
||||
ObiPathFrame section = smoother.GetSectionAt(1);
|
||||
waterEmitter.transform.position = transform.TransformPoint(section.position);
|
||||
waterEmitter.transform.rotation = transform.rotation * (Quaternion.LookRotation(section.tangent, section.binormal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d71cdcf242c9b44fb874af7b7d8fd2b9
|
||||
timeCreated: 1518879675
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
public class RopeBetweenTwoPoints : MonoBehaviour
|
||||
{
|
||||
public Transform start;
|
||||
public Transform end;
|
||||
public ObiSolver solver;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Generate rope synchronously:
|
||||
Generate();
|
||||
}
|
||||
|
||||
void Generate()
|
||||
{
|
||||
if (start != null && end != null)
|
||||
{
|
||||
// Adjust our transform:
|
||||
transform.position = (start.position + end.position) / 2;
|
||||
transform.rotation = Quaternion.FromToRotation(Vector3.right, end.position - start.position);
|
||||
|
||||
// Calculate control point positions and tangent vector:
|
||||
Vector3 startPositionLS = transform.InverseTransformPoint(start.position);
|
||||
Vector3 endPositionLS = transform.InverseTransformPoint(end.position);
|
||||
Vector3 tangentLS = (endPositionLS - startPositionLS).normalized;
|
||||
|
||||
// Create the blueprint:
|
||||
var blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
|
||||
|
||||
// Build the rope path:
|
||||
int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);
|
||||
blueprint.path.AddControlPoint(startPositionLS, -tangentLS, tangentLS, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "start");
|
||||
blueprint.path.AddControlPoint(endPositionLS, -tangentLS, tangentLS, Vector3.up, 0.1f, 0.1f, 1, filter, Color.white, "end");
|
||||
blueprint.path.FlushEvents();
|
||||
|
||||
// Generate particles/constraints:
|
||||
blueprint.GenerateImmediate();
|
||||
|
||||
// Add rope actor / renderer / attachment components:
|
||||
var rope = gameObject.AddComponent<ObiRope>();
|
||||
var ropeRenderer = gameObject.AddComponent<ObiRopeExtrudedRenderer>();
|
||||
var attachment1 = gameObject.AddComponent<ObiParticleAttachment>();
|
||||
var attachment2 = gameObject.AddComponent<ObiParticleAttachment>();
|
||||
|
||||
// Load the default rope section for rendering:
|
||||
ropeRenderer.section = Resources.Load<ObiRopeSection>("DefaultRopeSection");
|
||||
|
||||
// Set the blueprint:
|
||||
rope.ropeBlueprint = blueprint;
|
||||
|
||||
// Attach both ends:
|
||||
attachment1.target = start;
|
||||
attachment2.target = end;
|
||||
attachment1.particleGroup = blueprint.groups[0];
|
||||
attachment2.particleGroup = blueprint.groups[1];
|
||||
|
||||
// Parent the actor under a solver to start the simulation:
|
||||
transform.SetParent(solver.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9eaa9e75d3f0a4e119decb19dce797be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
129
Assets/Obi/Samples/RopeAndRod/SampleResources/Scripts/RopeNet.cs
Normal file
129
Assets/Obi/Samples/RopeAndRod/SampleResources/Scripts/RopeNet.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
/**
|
||||
* Sample component that procedurally generates a net using rigidbodies and ropes.
|
||||
*/
|
||||
public class RopeNet : MonoBehaviour
|
||||
{
|
||||
public Material material;
|
||||
|
||||
public Vector2Int resolution = new Vector2Int(5, 5);
|
||||
public Vector2 size = new Vector2(0.5f, 0.5f);
|
||||
public float nodeSize = 0.2f;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// create an object containing both the solver and the updater:
|
||||
GameObject solverObject = new GameObject("solver", typeof(ObiSolver), typeof(ObiFixedUpdater));
|
||||
ObiSolver solver = solverObject.GetComponent<ObiSolver>();
|
||||
ObiFixedUpdater updater = solverObject.GetComponent<ObiFixedUpdater>();
|
||||
updater.substeps = 2;
|
||||
|
||||
// adjust solver settings:
|
||||
solver.particleCollisionConstraintParameters.enabled = false;
|
||||
solver.distanceConstraintParameters.iterations = 8;
|
||||
solver.pinConstraintParameters.iterations = 4;
|
||||
solver.parameters.sleepThreshold = 0.001f;
|
||||
solver.PushSolverParameters();
|
||||
|
||||
// add the solver to the updater:
|
||||
updater.solvers.Add(solver);
|
||||
|
||||
// create the net (ropes + rigidbodies)
|
||||
CreateNet(solver);
|
||||
}
|
||||
|
||||
private void CreateNet(ObiSolver solver)
|
||||
{
|
||||
ObiCollider[,] nodes = new ObiCollider[resolution.x + 1, resolution.y + 1];
|
||||
|
||||
for (int x = 0; x <= resolution.x; ++x)
|
||||
{
|
||||
for (int y = 0; y <= resolution.y; ++y)
|
||||
{
|
||||
GameObject rb = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
rb.AddComponent<Rigidbody>();
|
||||
rb.transform.position = new Vector3(x, y, 0) * size;
|
||||
rb.transform.localScale = new Vector3(nodeSize, nodeSize, nodeSize);
|
||||
|
||||
nodes[x, y] = rb.AddComponent<ObiCollider>();
|
||||
nodes[x, y].Filter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
nodes[0, resolution.y].GetComponent<Rigidbody>().isKinematic = true;
|
||||
nodes[resolution.x, resolution.y].GetComponent<Rigidbody>().isKinematic = true;
|
||||
|
||||
for (int x = 0; x <= resolution.x; ++x)
|
||||
{
|
||||
for (int y = 0; y <= resolution.y; ++y)
|
||||
{
|
||||
Vector3 pos = new Vector3(x, y, 0) * size;
|
||||
if (x < resolution.x)
|
||||
{
|
||||
Vector3 offset = new Vector3(nodeSize * 0.5f, 0, 0);
|
||||
var rope = CreateRope(pos + offset, pos + new Vector3(size.x, 0, 0) - offset);
|
||||
rope.transform.parent = solver.transform;
|
||||
|
||||
PinRope(rope, nodes[x, y], nodes[x + 1, y], new Vector3(0.5f, 0, 0), -new Vector3(0.5f, 0, 0));
|
||||
}
|
||||
|
||||
if (y < resolution.y)
|
||||
{
|
||||
Vector3 offset = new Vector3(0, nodeSize * 0.5f, 0);
|
||||
var rope = CreateRope(pos + offset, pos + new Vector3(0, size.y, 0) - offset);
|
||||
rope.transform.parent = solver.transform;
|
||||
|
||||
PinRope(rope, nodes[x, y], nodes[x, y + 1], new Vector3(0, 0.5f, 0), -new Vector3(0, 0.5f, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PinRope(ObiRope rope, ObiCollider bodyA, ObiCollider bodyB, Vector3 offsetA, Vector3 offsetB)
|
||||
{
|
||||
// Pin both ends of the rope (this enables two-way interaction between character and rope):
|
||||
var pinConstraints = rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
|
||||
pinConstraints.Clear();
|
||||
var batch = new ObiPinConstraintsBatch();
|
||||
batch.AddConstraint(rope.solverIndices[0], bodyA, offsetA, Quaternion.identity, 0, 999, float.PositiveInfinity);
|
||||
batch.AddConstraint(rope.solverIndices[rope.activeParticleCount - 1], bodyB, offsetB, Quaternion.identity, 0, 999, float.PositiveInfinity);
|
||||
batch.activeConstraintCount = 2;
|
||||
pinConstraints.AddBatch(batch);
|
||||
}
|
||||
|
||||
// Creates a rope between two points in world space:
|
||||
private ObiRope CreateRope(Vector3 pointA, Vector3 pointB)
|
||||
{
|
||||
// Create a rope
|
||||
var ropeObject = new GameObject("solver", typeof(ObiRope), typeof(ObiRopeLineRenderer));
|
||||
var rope = ropeObject.GetComponent<ObiRope>();
|
||||
var ropeRenderer = ropeObject.GetComponent<ObiRopeLineRenderer>();
|
||||
rope.GetComponent<MeshRenderer>().material = material;
|
||||
rope.GetComponent<ObiPathSmoother>().decimation = 0.1f;
|
||||
ropeRenderer.uvScale = new Vector2(1, 5);
|
||||
|
||||
// Setup a blueprint for the rope:
|
||||
var blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
|
||||
blueprint.resolution = 0.15f;
|
||||
blueprint.thickness = 0.02f;
|
||||
blueprint.pooledParticles = 0;
|
||||
|
||||
// convert both points to the rope's local space:
|
||||
pointA = rope.transform.InverseTransformPoint(pointA);
|
||||
pointB = rope.transform.InverseTransformPoint(pointB);
|
||||
|
||||
// Procedurally generate the rope path (a simple straight line):
|
||||
Vector3 direction = (pointB - pointA) * 0.25f;
|
||||
blueprint.path.Clear();
|
||||
blueprint.path.AddControlPoint(pointA, -direction, direction, Vector3.up, 0.1f, 0.1f, 1, 1, Color.white, "A");
|
||||
blueprint.path.AddControlPoint(pointB, -direction, direction, Vector3.up, 0.1f, 0.1f, 1, 1, Color.white, "B");
|
||||
blueprint.path.FlushEvents();
|
||||
|
||||
rope.ropeBlueprint = blueprint;
|
||||
return rope;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83e341aa1a9a14825ab84ad04cd5f44c
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
[RequireComponent(typeof(ObiRope))]
|
||||
public class RopeSweepCut : MonoBehaviour
|
||||
{
|
||||
|
||||
public Camera cam;
|
||||
|
||||
ObiRope rope;
|
||||
LineRenderer lineRenderer;
|
||||
Vector3 cutStartPosition;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
|
||||
AddMouseLine();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DeleteMouseLine();
|
||||
}
|
||||
|
||||
private void AddMouseLine()
|
||||
{
|
||||
GameObject line = new GameObject("Mouse Line");
|
||||
lineRenderer = line.AddComponent<LineRenderer>();
|
||||
lineRenderer.startWidth = 0.005f;
|
||||
lineRenderer.endWidth = 0.005f;
|
||||
lineRenderer.numCapVertices = 2;
|
||||
lineRenderer.sharedMaterial = new Material(Shader.Find("Unlit/Color"));
|
||||
lineRenderer.sharedMaterial.color = Color.cyan;
|
||||
lineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
private void DeleteMouseLine()
|
||||
{
|
||||
if (lineRenderer != null)
|
||||
Destroy(lineRenderer.gameObject);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// do nothing if we don't have a camera to cut from.
|
||||
if (cam == null) return;
|
||||
|
||||
// process user input and cut the rope if necessary.
|
||||
ProcessInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Very simple mouse-based input. Not ideal for multitouch screens as it only supports one finger, though.
|
||||
*/
|
||||
private void ProcessInput()
|
||||
{
|
||||
// When the user clicks the mouse, start a line cut:
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
cutStartPosition = Input.mousePosition;
|
||||
lineRenderer.SetPosition(0, cam.ScreenToWorldPoint(new Vector3(cutStartPosition.x, cutStartPosition.y, 0.5f)));
|
||||
lineRenderer.enabled = true;
|
||||
}
|
||||
|
||||
if (lineRenderer.enabled)
|
||||
lineRenderer.SetPosition(1, cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0.5f)));
|
||||
|
||||
// When the user lifts the mouse, proceed to cut.
|
||||
if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
ScreenSpaceCut(cutStartPosition, Input.mousePosition);
|
||||
lineRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cuts the rope using a line segment, expressed in screen-space.
|
||||
*/
|
||||
private void ScreenSpaceCut(Vector2 lineStart, Vector2 lineEnd)
|
||||
{
|
||||
// keep track of whether the rope was cut or not.
|
||||
bool cut = false;
|
||||
|
||||
// iterate over all elements and test them for intersection with the line:
|
||||
for (int i = 0; i < rope.elements.Count; ++i)
|
||||
{
|
||||
// project the both ends of the element to screen space.
|
||||
Vector3 screenPos1 = cam.WorldToScreenPoint(rope.solver.positions[rope.elements[i].particle1]);
|
||||
Vector3 screenPos2 = cam.WorldToScreenPoint(rope.solver.positions[rope.elements[i].particle2]);
|
||||
|
||||
// test if there's an intersection:
|
||||
if (SegmentSegmentIntersection(screenPos1, screenPos2, lineStart, lineEnd, out float r, out float s))
|
||||
{
|
||||
cut = true;
|
||||
rope.Tear(rope.elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// If the rope was cut at any point, rebuilt constraints:
|
||||
if (cut) rope.RebuildConstraintsFromElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* line segment 1 is AB = A+r(B-A)
|
||||
* line segment 2 is CD = C+s(D-C)
|
||||
* if they intesect, then A+r(B-A) = C+s(D-C), solving for r and s gives the formula below.
|
||||
* If both r and s are in the 0,1 range, it meant the segments intersect.
|
||||
*/
|
||||
private bool SegmentSegmentIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D, out float r, out float s)
|
||||
{
|
||||
float denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
|
||||
float rNum = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
|
||||
float sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
|
||||
|
||||
if (Mathf.Approximately(rNum, 0) || Mathf.Approximately(denom, 0))
|
||||
{ r = -1; s = -1; return false; }
|
||||
|
||||
r = rNum / denom;
|
||||
s = sNum / denom;
|
||||
|
||||
return (r >= 0 && r <=1 && s >= 0 && s <= 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88cb6f64c607f46b3b0d7a30a99ed18b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class RopeTenser : MonoBehaviour
|
||||
{
|
||||
public float force = 10;
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
GetComponent<Rigidbody>().AddForce(Vector3.down * force);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a01ab42f187a43818168cd1cd11d3cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
[RequireComponent(typeof(ObiRope))]
|
||||
[RequireComponent(typeof(MeshRenderer))]
|
||||
public class RopeTensionColorizer : MonoBehaviour
|
||||
{
|
||||
public float minTension = 0;
|
||||
public float maxTension = 0.2f;
|
||||
public Color normalColor = Color.green;
|
||||
public Color tensionColor = Color.red;
|
||||
|
||||
public RopeTenser tenser;
|
||||
public float tenserThreshold = -5;
|
||||
public float tenserMax = 0.1f;
|
||||
|
||||
private ObiRope rope;
|
||||
private Material localMaterial;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
localMaterial = GetComponent<MeshRenderer>().material;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Destroy(localMaterial);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (tenser == null)
|
||||
return;
|
||||
|
||||
// Calculate how much past the threshold the tenser is, clamp the excess to 1
|
||||
float tenserFactor = Mathf.Min((tenser.transform.position.y - tenserThreshold) / tenserMax,1);
|
||||
|
||||
// Check if the tenser is above the threshold, if so then check rope tension:
|
||||
if (tenserFactor > 0)
|
||||
{
|
||||
// Calculate current tension as ratio between current and rest length, then subtract 1 to center it at zero.
|
||||
float tension = rope.CalculateLength() / rope.restLength - 1;
|
||||
|
||||
// Use tension to interpolate between colors:
|
||||
float lerpFactor = (tension - minTension) / (maxTension - minTension);
|
||||
localMaterial.color = Color.Lerp(normalColor, tensionColor, lerpFactor * tenserFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
localMaterial.color = normalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51f5e34b2316b45c6bdf971b375b5f2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
public class RuntimeRopeGenerator
|
||||
{
|
||||
//private ObiRope rope;
|
||||
//private ObiRopeCursor cursor;
|
||||
private ObiSolver solver;
|
||||
private int pinnedParticle = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a straight rope anchored to a transform at the top.
|
||||
/// Transform may or may not move around and may or may not have a rigidbody.
|
||||
/// When you call this the rope will appear in the scene and immediately interact with gravity and objects with ObiColliders.
|
||||
/// Called from anywhere (main thread only)
|
||||
/// </summary>
|
||||
public IEnumerator MakeRope(Transform anchoredTo, Vector3 attachmentOffset, float ropeLength)
|
||||
{
|
||||
// create a new GameObject with the required components: a solver, a rope, and a curve.
|
||||
// we also throw a cursor in to be able to change its length.
|
||||
/*GameObject ropeObject = new GameObject("rope",typeof(ObiSolver),
|
||||
typeof(ObiRope),
|
||||
typeof(ObiCurve),
|
||||
typeof (ObiRopeCursor));
|
||||
|
||||
// get references to all components:
|
||||
rope = ropeObject.GetComponent<ObiRope>();
|
||||
cursor = ropeObject.GetComponent<ObiRopeCursor>();
|
||||
solver = ropeObject.GetComponent<ObiSolver>();
|
||||
ObiCurve path = ropeObject.GetComponent<ObiCurve>();
|
||||
|
||||
// set up component references (see ObiRopeHelper.cs)
|
||||
rope.Solver = solver;
|
||||
rope.ropePath = path;
|
||||
//rope.section = Resources.Load<ObiRopeSection>("DefaultRopeSection");
|
||||
|
||||
// set path control points (duplicate end points, to set curvature as required by CatmullRom splines):
|
||||
path.controlPoints.Clear();
|
||||
path.controlPoints.Add(new ObiCurve.ControlPoint(Vector3.zero,Vector3.up));
|
||||
path.controlPoints.Add(new ObiCurve.ControlPoint(Vector3.zero,Vector3.up));
|
||||
path.controlPoints.Add(new ObiCurve.ControlPoint(Vector3.down*ropeLength,Vector3.up));
|
||||
path.controlPoints.Add(new ObiCurve.ControlPoint(Vector3.down*ropeLength,Vector3.up));
|
||||
|
||||
rope.pooledParticles = 2000;
|
||||
|
||||
// parent the rope to the anchor transform:
|
||||
rope.transform.SetParent(anchoredTo,false);
|
||||
rope.transform.localPosition = attachmentOffset;
|
||||
|
||||
// generate particles/constraints and add them to the solver (see ObiRopeHelper.cs)
|
||||
yield return rope.StartCoroutine(rope.GeneratePhysicRepresentationForMesh());
|
||||
rope.AddToSolver(null);
|
||||
|
||||
// get the last particle in the rope at its rest state.
|
||||
pinnedParticle = rope.UsedParticles-1;
|
||||
|
||||
// add a tethers batch:
|
||||
ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true,false,0,1);
|
||||
rope.TetherConstraints.AddBatch(tetherBatch);
|
||||
//UpdateTethers();
|
||||
|
||||
// fix first particle in place (see http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html)
|
||||
rope.Solver.invMasses[rope.particleIndices[0]] = rope.invMasses[0] = 0;*/
|
||||
yield return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MakeRope and AddPendulum may NOT be called on the same frame. You must wait for the MakeRope coroutine to finish first, as creating a rope is an asynchronous operation.
|
||||
/// Just adds a pendulum to the rope on the un-anchored end.
|
||||
/// </summary>
|
||||
public void AddPendulum(ObiCollider pendulum, Vector3 attachmentOffset)
|
||||
{
|
||||
// simply add a new pin constraint (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
|
||||
/*rope.PinConstraints.RemoveFromSolver(null);
|
||||
ObiPinConstraintBatch batch = (ObiPinConstraintBatch)rope.PinConstraints.GetFirstBatch();
|
||||
batch.AddConstraint(pinnedParticle, pendulum, attachmentOffset,Quaternion.identity, 1);
|
||||
rope.PinConstraints.AddToSolver(null);*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RemovePendulum and AddPendulum may be called on the same frame.
|
||||
/// </summary>
|
||||
public void RemovePendulum()
|
||||
{
|
||||
// simply remove all pin constraints (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
|
||||
/*rope.PinConstraints.RemoveFromSolver(null);
|
||||
rope.PinConstraints.GetFirstBatch().Clear();
|
||||
rope.PinConstraints.AddToSolver(null);*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like extending or retracting a winch.
|
||||
/// </summary>
|
||||
public void ChangeRopeLength(float changeAmount)
|
||||
{
|
||||
// the cursor will automatically add/remove/modify constraints and particles as needed to obtain the new length.
|
||||
//cursor.ChangeLength(rope.RestLength + changeAmount);
|
||||
//UpdateTethers();
|
||||
}
|
||||
|
||||
private void UpdateTethers()
|
||||
{
|
||||
/*rope.TetherConstraints.RemoveFromSolver(null);
|
||||
ObiTetherConstraintBatch batch = (ObiTetherConstraintBatch)rope.TetherConstraints.GetFirstBatch();
|
||||
batch.Clear();
|
||||
|
||||
ObiDistanceConstraintBatch dbatch = rope.DistanceConstraints.GetFirstBatch();
|
||||
for (int i = 0; i < dbatch.ConstraintCount; ++i)
|
||||
batch.AddConstraint(0,dbatch.springIndices[i*2+1], rope.InterparticleDistance*i, 1, 1);
|
||||
|
||||
batch.Cook();
|
||||
rope.TetherConstraints.AddToSolver(null);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0854563331ce443fa9b17a143c084806
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
public class RuntimeRopeGeneratorUse : MonoBehaviour
|
||||
{
|
||||
public ObiCollider pendulum;
|
||||
RuntimeRopeGenerator rg;
|
||||
|
||||
public IEnumerator Start()
|
||||
{
|
||||
rg = new RuntimeRopeGenerator();
|
||||
|
||||
// Create a rope:
|
||||
yield return rg.MakeRope(transform,Vector3.zero,1);
|
||||
|
||||
// Add a pendulum (you should adjust the attachment point depending on your particular pendulum object)
|
||||
rg.AddPendulum(pendulum,Vector3.up*0.5f);
|
||||
}
|
||||
|
||||
public void Update(){
|
||||
|
||||
if (Input.GetKey(KeyCode.W)){
|
||||
rg.ChangeRopeLength(- Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.S)){
|
||||
rg.ChangeRopeLength( Time.deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e7b946ba720c495a8ee7c1d3e903063
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
public class SnakeController : MonoBehaviour
|
||||
{
|
||||
public Transform headReferenceFrame;
|
||||
public float headSpeed = 20;
|
||||
public float upSpeed = 40;
|
||||
public float slitherSpeed = 10;
|
||||
|
||||
private ObiRope rope;
|
||||
private ObiSolver solver;
|
||||
private float[] traction;
|
||||
private Vector3[] surfaceNormal;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
solver = rope.solver;
|
||||
|
||||
// initialize traction array:
|
||||
traction = new float[rope.activeParticleCount];
|
||||
surfaceNormal = new Vector3[rope.activeParticleCount];
|
||||
|
||||
// subscribe to solver events (to update surface information)
|
||||
solver.OnBeginStep += ResetSurfaceInfo;
|
||||
solver.OnCollision += AnalyzeContacts;
|
||||
solver.OnParticleCollision += AnalyzeContacts;
|
||||
}
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
solver.OnBeginStep -= ResetSurfaceInfo;
|
||||
solver.OnCollision -= AnalyzeContacts;
|
||||
solver.OnParticleCollision -= AnalyzeContacts;
|
||||
}
|
||||
|
||||
private void ResetSurfaceInfo(ObiSolver s, float stepTime)
|
||||
{
|
||||
// reset surface info:
|
||||
for (int i = 0; i < traction.Length; ++i)
|
||||
{
|
||||
traction[i] = 0;
|
||||
surfaceNormal[i] = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeContacts(object sender, ObiSolver.ObiCollisionEventArgs e)
|
||||
{
|
||||
// iterate trough all contacts:
|
||||
for (int i = 0; i < e.contacts.Count; ++i)
|
||||
{
|
||||
var contact = e.contacts.Data[i];
|
||||
if (contact.distance < 0.005f)
|
||||
{
|
||||
int simplexIndex = solver.simplices[contact.bodyA];
|
||||
var particleInActor = solver.particleToActor[simplexIndex];
|
||||
|
||||
if (particleInActor.actor == rope)
|
||||
{
|
||||
// using 1 here, could calculate a traction value based on the type of terrain, friction, etc.
|
||||
traction[particleInActor.indexInActor] = 1;
|
||||
|
||||
// accumulate surface normal:
|
||||
surfaceNormal[particleInActor.indexInActor] += (Vector3)contact.normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Input.GetKey(KeyCode.J))
|
||||
{
|
||||
for (int i = 1; i < rope.activeParticleCount; ++i)
|
||||
{
|
||||
int solverIndex = rope.solverIndices[i];
|
||||
int prevSolverIndex = rope.solverIndices[i - 1];
|
||||
|
||||
// direction from current particle to previous one, projected on the contact surface:
|
||||
Vector4 dir = Vector3.ProjectOnPlane(solver.positions[prevSolverIndex] - solver.positions[solverIndex], surfaceNormal[i]).normalized;
|
||||
|
||||
// move in that direction:
|
||||
solver.velocities[solverIndex] += dir * traction[i] * slitherSpeed * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
int headIndex = rope.solverIndices[0];
|
||||
|
||||
if (headReferenceFrame != null)
|
||||
{
|
||||
Vector3 direction = Vector3.zero;
|
||||
|
||||
// Determine movement direction:
|
||||
if (Input.GetKey(KeyCode.W))
|
||||
{
|
||||
direction += headReferenceFrame.forward * headSpeed;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.A))
|
||||
{
|
||||
direction += -headReferenceFrame.right * headSpeed;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.S))
|
||||
{
|
||||
direction += -headReferenceFrame.forward * headSpeed;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.D))
|
||||
{
|
||||
direction += headReferenceFrame.right * headSpeed;
|
||||
}
|
||||
|
||||
// flatten out the direction so that it's parallel to the ground:
|
||||
direction.y = 0;
|
||||
|
||||
solver.velocities[headIndex] += (Vector4)direction * Time.deltaTime;
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.Space))
|
||||
solver.velocities[headIndex] += (Vector4)Vector3.up * Time.deltaTime * upSpeed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5d46d2f0b9f9496f9ed59231aa6982d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Obi;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class SpiralCurve : MonoBehaviour {
|
||||
|
||||
public float radius = 0.25f;
|
||||
public float radialStep = 0.8f;
|
||||
public float heightStep = 0.04f;
|
||||
public float points = 30;
|
||||
|
||||
public float rotationalMass = 1;
|
||||
public float thickness = 1;
|
||||
|
||||
void Awake ()
|
||||
{
|
||||
Generate();
|
||||
}
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
var rod = GetComponent<ObiRopeBase>();
|
||||
if (rod == null) return;
|
||||
|
||||
var blueprint = rod.sourceBlueprint as ObiRopeBlueprintBase;
|
||||
if (blueprint == null) return;
|
||||
|
||||
blueprint.path.Clear();
|
||||
|
||||
float ang = 0;
|
||||
float height = 0;
|
||||
|
||||
for (int i = 0; i < points; ++i)
|
||||
{
|
||||
Vector3 point = new Vector3(Mathf.Cos(ang) * radius, height, Mathf.Sin(ang) * radius);
|
||||
|
||||
// optimal handle length for circle approximation: 4/3 tan(pi/(2n))
|
||||
Vector3 tangent = new Vector3(-point.z, heightStep, point.x).normalized * (4.0f / 3.0f) * Mathf.Tan(radialStep / 4.0f) * radius;
|
||||
|
||||
blueprint.path.AddControlPoint(point, -tangent, tangent, Vector3.up, 1, rotationalMass, thickness, 1, Color.white, "control point " + i);
|
||||
ang += radialStep;
|
||||
height += heightStep;
|
||||
}
|
||||
|
||||
blueprint.path.FlushEvents();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e76488b5e11ef47ba9c0746db5c47115
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Obi;
|
||||
|
||||
[RequireComponent(typeof(ObiSolver))]
|
||||
public class WrapRopeGameController : MonoBehaviour
|
||||
{
|
||||
|
||||
ObiSolver solver;
|
||||
|
||||
public Wrappable[] wrappables;
|
||||
public UnityEvent onFinish = new UnityEvent();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
solver = GetComponent<ObiSolver>();
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
void OnEnable()
|
||||
{
|
||||
solver.OnCollision += Solver_OnCollision;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
solver.OnCollision -= Solver_OnCollision;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
bool allWrapped = true;
|
||||
|
||||
// Test our win condition: all pegs must be wrapped.
|
||||
foreach (var wrappable in wrappables)
|
||||
{
|
||||
if (!wrappable.IsWrapped())
|
||||
{
|
||||
allWrapped = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allWrapped)
|
||||
onFinish.Invoke();
|
||||
}
|
||||
|
||||
private void Solver_OnCollision(ObiSolver s, ObiSolver.ObiCollisionEventArgs e)
|
||||
{
|
||||
// reset to unwrapped state:
|
||||
foreach (var wrappable in wrappables)
|
||||
wrappable.Reset();
|
||||
|
||||
var world = ObiColliderWorld.GetInstance();
|
||||
foreach (Oni.Contact contact in e.contacts)
|
||||
{
|
||||
// look for actual contacts only:
|
||||
if (contact.distance < 0.025f)
|
||||
{
|
||||
var col = world.colliderHandles[contact.bodyB].owner;
|
||||
if (col != null)
|
||||
{
|
||||
var wrappable = col.GetComponent<Wrappable>();
|
||||
if (wrappable != null)
|
||||
wrappable.SetWrapped();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92b1832c67f564e9fa43a180598111e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class WrapRopePlayerController : MonoBehaviour
|
||||
{
|
||||
public float acceleration = 50;
|
||||
Rigidbody rb;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rb = GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
Vector3 direction = Vector3.zero;
|
||||
|
||||
// Determine movement direction:
|
||||
if (Input.GetKey(KeyCode.W))
|
||||
{
|
||||
direction += Vector3.up * acceleration;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.A))
|
||||
{
|
||||
direction += Vector3.left * acceleration;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.S))
|
||||
{
|
||||
direction += Vector3.down * acceleration;
|
||||
}
|
||||
if (Input.GetKey(KeyCode.D))
|
||||
{
|
||||
direction += Vector3.right * acceleration;
|
||||
}
|
||||
|
||||
rb.AddForce(direction.normalized * acceleration, ForceMode.Acceleration);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdf58ad80e1cd4860aa12e8da0aa833c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Wrappable : MonoBehaviour
|
||||
{
|
||||
|
||||
private bool wrapped = false;
|
||||
public Color normalColor = new Color(0.2f,0.2f,0.8f);
|
||||
public Color wrappedColor = new Color(0.9f, 0.9f, 0.2f);
|
||||
|
||||
Material localMaterial;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
localMaterial = GetComponent<MeshRenderer>().material;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
Destroy(localMaterial);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
wrapped = false;
|
||||
localMaterial.color = normalColor;
|
||||
}
|
||||
|
||||
public void SetWrapped()
|
||||
{
|
||||
wrapped = true;
|
||||
localMaterial.color = wrappedColor;
|
||||
}
|
||||
|
||||
public bool IsWrapped()
|
||||
{
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39f0f8f454a2049f3af7bad3dda0481e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user