重新导入obi

This commit is contained in:
2026-04-06 11:35:18 +08:00
parent 05fa2d6e5e
commit ae3002a0e2
1643 changed files with 232496 additions and 13 deletions

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 82b0f164ac14247fd8a566109fbbe771
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5551d37f45fd044ff8768019501c20a5
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 83c7e82b3aac04e408bc8bfe9027c983
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: b77478c97cefb4dec9cc85236f7c5fca
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 94f9aa141b5964f9f91dadcc1919618c
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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));
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d71cdcf242c9b44fb874af7b7d8fd2b9
timeCreated: 1518879675
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9eaa9e75d3f0a4e119decb19dce797be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 83e341aa1a9a14825ab84ad04cd5f44c
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88cb6f64c607f46b3b0d7a30a99ed18b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a01ab42f187a43818168cd1cd11d3cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51f5e34b2316b45c6bdf971b375b5f2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);*/
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 0854563331ce443fa9b17a143c084806
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5e7b946ba720c495a8ee7c1d3e903063
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5d46d2f0b9f9496f9ed59231aa6982d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: e76488b5e11ef47ba9c0746db5c47115
labels:
- ObiRope
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92b1832c67f564e9fa43a180598111e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fdf58ad80e1cd4860aa12e8da0aa833c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39f0f8f454a2049f3af7bad3dda0481e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: