Skip to content

Instantly share code, notes, and snippets.

@Garfounkel
Last active December 13, 2020 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Garfounkel/be99b53f7ce62cf5d7653a4f9fcea331 to your computer and use it in GitHub Desktop.
Save Garfounkel/be99b53f7ce62cf5d7653a4f9fcea331 to your computer and use it in GitHub Desktop.
Collection of utilities solving specific problems for Unity

Utilities for Unity

Files in this gist are things that do not exactly fit in a general utility repository because I don't use them in every single project, but they are generic enough that someone else might also want to use them in their own project.

License

You are free to use and/or sell copies of this software according to the following MIT License. Credits are not required, but greately appreciated.

MIT License

Copyright (c) 2020 Simon Andersen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

In layman terms: https://choosealicense.com/licenses/mit/

// ----------------------------------------------------------------------------
// Author: Simon Andersen
// Date: 12/12/2020
// ----------------------------------------------------------------------------
//
// The Following code will not work on its own and is just an example of what a CheckSlope function
// could look like in a CharacterController2D script (attached to the player GameObject).
//
// CheckSlope() should called sometime during Update().
// First it checks if the player was stopped last frame while trying to move forward by holding a
// direction button. If it's the case then it checks if there is a slope in front of the player.
// If there is a slope smaller than the maxSlope the player can climb, then it checks if the spot
// on top of the slope is free. If it's the case then it moves the player on top of the slope and
// the player can start moving again.
private (Vector2 pos, float input) _lastUpdate = default;
private void CheckSlope()
{
if (_horizontalAxis != 0 && _lastUpdate.input != 0 &&
Vector2.Distance(transform.position, _lastUpdate.pos) < 0.0001f)
{
var slopePointCheck = groundCheck.position.WithXPlus(0.5f * (FacingRight?1:-1)).WithYPlus(maxSlope);
var checkSlopeFront = Physics2D.OverlapCircle(slopePointCheck, 0.01f, groundLayers);
if (!checkSlopeFront)
{
var hit = Physics2D.Raycast(slopePointCheck, Vector2.down, groundLayers);
if (hit)
transform.position = transform.position.WithY(hit.point.y + 0.5f).WithXPlus(0.1f * (FacingRight?1:-1));
}
}
_lastUpdate = (transform.position, _horizontalAxis);
}
// ----------------------------------------------------------------------------
// Author: Simon Andersen
// Date: 2/11/2020
// ----------------------------------------------------------------------------
//
// Attach this component to a gameobject with either an Edge/Circle/CapsuleCollider2D or a CompositeCollider2D and this
// will automatically generate a child collider with a geometry that is an exact copy of the geometry of this collider.
//
// -- Note on the CompositeCollider2D:
// Any Box/Polygon/TilemapCollider2D can be used by a CompositeCollider2D, meaning that with this utility we can
// generate any collider composed of a combination of one or more Box/Polygon/TilemapCollider2D. A CompositeCollider2D
// always requires to attach a Rigidbody2D to the gameobject but remember you can always set the body type of the
// RigidBody as static if you don't need it for anything else.
#if UNITY_EDITOR
using System.Linq;
using UnityEditor;
using UnityEngine;
[ExecuteInEditMode]
public class GenerateCollider : MonoBehaviour
{
[Tooltip("When activated, the child collider will be re-generated everytime this gameobject is modified while in " +
"edit mode. If this is slow, disable this and use the Generate button manually.")]
public bool automaticGeneration;
private GameObject _child;
private CompositeCollider2D _compositeCollider;
private EdgeCollider2D _edgeCollider;
private CircleCollider2D _circleCollider;
private CapsuleCollider2D _capsuleCollider;
private void Update()
{
if (!automaticGeneration || Application.isPlaying || Selection.activeGameObject != gameObject) return;
Generate();
}
public void Generate()
{
// Cleanup previous generated colliders before generating
const string childName = "GeneratedCollider";
(from Transform child in transform select child.gameObject).ToList().ForEach(child =>
{
if (child.name.Contains(childName))
DestroyImmediate(child);
});
_compositeCollider = GetComponent<CompositeCollider2D>();
_edgeCollider = GetComponent<EdgeCollider2D>();
_circleCollider = GetComponent<CircleCollider2D>();
_capsuleCollider = GetComponent<CapsuleCollider2D>();
_child = new GameObject(childName);
_child.transform.SetParent(transform, worldPositionStays:false);
if (_edgeCollider != null)
GenerateEdgeCollider();
else if (_circleCollider != null)
GenerateCircleCollider();
else if (_capsuleCollider != null)
GenerateCapsuleCollider();
else
GeneratePolygonCollider();
}
#region Per Collider type generate function
private void GenerateEdgeCollider()
{
var childCollider = _child.AddComponent<EdgeCollider2D>();
childCollider.SetPoints(_edgeCollider.points.ToList());
childCollider.offset = _edgeCollider.offset;
childCollider.edgeRadius = _edgeCollider.edgeRadius;
}
private void GenerateCircleCollider()
{
var childCollider = _child.AddComponent<CircleCollider2D>();
childCollider.radius = _circleCollider.radius;
childCollider.offset = _circleCollider.offset;
}
private void GenerateCapsuleCollider()
{
var childCollider = _child.AddComponent<CapsuleCollider2D>();
childCollider.size = _capsuleCollider.size;
childCollider.offset = _capsuleCollider.offset;
childCollider.direction = _capsuleCollider.direction;
}
// This is used for any combination of one or more Box/Polygon/TilemapCollider2D with a CompositeCollider2D
private void GeneratePolygonCollider()
{
var childCollider = _child.AddComponent<PolygonCollider2D>();
childCollider.pathCount = 0;
foreach (var i in Enumerable.Range(0, _compositeCollider.pathCount))
{
var points = new Vector2[_compositeCollider.GetPathPointCount(i)];
_compositeCollider.GetPath(i, points);
childCollider.pathCount++;
childCollider.SetPath(i, points);
}
}
#endregion
}
#region Custom Editor
[CustomEditor(typeof(GenerateCollider))]
public class GenerateColliderEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
var baseScript = (GenerateCollider)target;
if (GUILayout.Button(nameof(baseScript.Generate)))
baseScript.Generate();
}
}
#endregion
#endif
MIT License
Copyright (c) 2020 Simon Andersen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment