Fast moving objects 2D game (Unity3d) - performance

I find the official unity training https://www.youtube.com/watch?v=D5MqLcO6A8g
and find bug.(look at the score)
I spent about 2 days to fix it and failed.
I find the "DontGoThroughThings" script and try to rewrite to use in 2D. Failed again)
Please help me!
This is rewrite script:
public LayerMask layerMask; //make sure we aren't in this layer
public float skinWidth; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector2 previousPosition;
private Rigidbody2D myRigidbody;
//initialize values
void Awake()
{
myRigidbody = GetComponent<Rigidbody2D>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(GetComponent<Collider2D>().bounds.extents.x, GetComponent<Collider2D>().bounds.extents.y));
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector2 movementThisStep = myRigidbody.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
//RaycastHit2D hitInfo;
//check for obstructions we might have missed
if (Physics2D.Raycast(previousPosition, movementThisStep, movementMagnitude, 0, layerMask.value))
myRigidbody.position = (movementThisStep/movementMagnitude)*partialExtent;
Debug.DrawLine(myRigidbody.position, myRigidbody.position - previousPosition, Color.green);
}
previousPosition = myRigidbody.position;
}
This is unitypackage https://www.dropbox.com/s/a3n1dalbc1k0k42/Hat%20Trick.unitypackage?dl=0
P.S. Sorry for my english and thank you for help!!

Explanation
Continuous collision detection in Unity does not use raycasting. As a result, very fast moving (and/or comparatively small) objects (let's call that kind of object projectile for now) still pass through things without a collision being detected. The famous DontGoThroughThings component fixes that for 3D. There are a few inconsistencies, but it can get the job done if you know what you are doing.
Here is my 2D adaption of it.
I added some features to make it more user-friendly to everyone who is not that good at coding or game physics.
How to use it
Add this component to your fast moving objects and they will always trigger the OnTriggerEnter2D event when hitting something.
You can also chose to send a different, custom message instead (by changing the MessageName variable). I actually recommend that due to the caveat explained below.
Sending messages is the script's primary use case. It will not magically make a projectile behave correctly in a physical sense.
The triggerTarget variable determines whether it sends the message to itself (in case, you have the hit handling script attached to the projectile), to the object being hit (in case, you have the hit handling attached to the objects that should be hit by projectiles), or any variation of the two.
Unlike the original version, this script also allows for force to be applied upon impact which can be tuned through the momentumTransferFraction variable. When two objects collide the force generated is a result of a transfer of momentum (mass times velocity) between the two objects. The way I do this is very rudimentary and it is missing a lot of contributing factors, but it is enough to have projectiles push objects on impact. Just like in the real world, the faster or heavier your projectile is, the more force is exerted.
There are also some caveats (most of which also apply to the original version)
Only apply on this on very fast moving objects. The less you use it the better because it is quite a bit more computationally expensive than the normal collision detection.
While collision detection is more accurate, Collision resolution is only very rudimentary. It is not as good as what the physics engine does by default.
In the current version, collisions are always detected in hindsight. That is why you might see projectiles having gone through the object by the time the collision is registered. I want to fix that in the near future.
If you use this on objects like bullets or other forms of projectiles that basically stop working after first hit, you can set momentumTransferFraction to 1 to let the bullet physically push the object (by applying all its momentum to the first hit object) without the bullet being affected itself.
For some reason, you cannot disable default collision detection just for one object. This means that if you are so (un-)lucky and a collision happens to be registered by Unity's default collision checks, you might have OnTriggerEnter2D fire multiple times on the same object, or (if collider is not a trigger) exert a force on hit targets (in addition to the one exerted by this script). However, since that would be somewhat random and very inconsistent, in addition to turning on IsTrigger on your projectile's collider, I recommend using a custom message name to handle projectile impacts. This way, collisions that are randomly detected by default collision detection would not have any unintended side effects [Remember that default collision detection being inconsistent for these sorts of objects is the actual reason why you add this script]. FYI: As of Unity 5, the only two ways to prevent default collision detection are IgnoreCollision and IgnoreLayerCollision.
Code
using UnityEngine;
using System.Collections;
using System.Linq;
/// <summary>
/// 2D adaption of the famous DontGoThroughThings component (http://wiki.unity3d.com/index.php?title=DontGoThroughThings).
/// Uses raycasting to trigger OnTriggerEnter2D events when hitting something.
/// </summary>
/// <see cref="http://stackoverflow.com/a/29564394/2228771"/>
public class ProjectileCollisionTrigger2D : MonoBehaviour {
public enum TriggerTarget {
None = 0,
Self = 1,
Other = 2,
Both = 3
}
/// <summary>
/// The layers that can be hit by this object.
/// Defaults to "Everything" (-1).
/// </summary>
public LayerMask hitLayers = -1;
/// <summary>
/// The name of the message to be sent on hit.
/// You generally want to change this, especially if you want to let the projectile apply a force (`momentumTransferFraction` greater 0).
/// If you do not change this, the physics engine (when it happens to pick up the collision)
/// will send an extra message, prior to this component being able to. This might cause errors or unexpected behavior.
/// </summary>
public string MessageName = "OnTriggerEnter2D";
/// <summary>
/// Where to send the hit event message to.
/// </summary>
public TriggerTarget triggerTarget = TriggerTarget.Both;
/// <summary>
/// How much of momentum is transfered upon impact.
/// If set to 0, no force is applied.
/// If set to 1, the entire momentum of this object is transfered upon the first collider and this object stops dead.
/// If set to anything in between, this object will lose some velocity and transfer the corresponding momentum onto every collided object.
/// </summary>
public float momentumTransferFraction = 0;
private float minimumExtent;
private float sqrMinimumExtent;
private Vector2 previousPosition;
private Rigidbody2D myRigidbody;
private Collider2D myCollider;
//initialize values
void Awake()
{
myRigidbody = GetComponent<Rigidbody2D>();
myCollider = GetComponents<Collider2D> ().FirstOrDefault();
if (myCollider == null || myRigidbody == null) {
Debug.LogError("ProjectileCollisionTrigger2D is missing Collider2D or Rigidbody2D component", this);
enabled = false;
return;
}
previousPosition = myRigidbody.transform.position;
minimumExtent = Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
var origPosition = transform.position;
Vector2 movementThisStep = (Vector2)transform.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent) {
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
//check for obstructions we might have missed
RaycastHit2D[] hitsInfo = Physics2D.RaycastAll(previousPosition, movementThisStep, movementMagnitude, hitLayers.value);
//Going backward because we want to look at the first collisions first. Because we want to destroy the once that are closer to previous position
for (int i = 0; i < hitsInfo.Length; ++i) {
var hitInfo = hitsInfo[i];
if (hitInfo && hitInfo.collider != myCollider) {
// apply force
if (hitInfo.rigidbody && momentumTransferFraction != 0) {
// When using impulse mode, the force argument is actually the amount of instantaneous momentum transfered.
// Quick physics refresher: F = dp / dt = m * dv / dt
// Note: dt is the amount of time traveled (which is the time of the current frame and is taken care of internally, when using impulse mode)
// For more info, go here: http://forum.unity3d.com/threads/rigidbody2d-forcemode-impulse.213397/
var dv = myRigidbody.velocity;
var m = myRigidbody.mass;
var dp = dv * m;
var impulse = momentumTransferFraction * dp;
hitInfo.rigidbody.AddForceAtPosition(impulse, hitInfo.point, ForceMode2D.Impulse);
if (momentumTransferFraction < 1) {
// also apply force to self (in opposite direction)
var impulse2 = (1-momentumTransferFraction) * dp;
hitInfo.rigidbody.AddForceAtPosition(-impulse2, hitInfo.point, ForceMode2D.Impulse);
}
}
// move this object to point of collision
transform.position = hitInfo.point;
// send hit messages
if (((int)triggerTarget & (int)TriggerTarget.Other) != 0 && hitInfo.collider.isTrigger) {
hitInfo.collider.SendMessage(MessageName, myCollider, SendMessageOptions.DontRequireReceiver);
}
if (((int)triggerTarget & (int)TriggerTarget.Self) != 0) {
SendMessage(MessageName, hitInfo.collider, SendMessageOptions.DontRequireReceiver);
}
}
}
}
previousPosition = transform.position = origPosition;
}
}

Here is the 2D version I rewrote of this script (for Unity 4.6):
using UnityEngine;
using System.Collections;
public class DontGoThroughThings : MonoBehaviour
{
public delegate void CollidedDelegate(Collider2D collider);
public event CollidedDelegate Collided;
public LayerMask layerMask; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector2 previousPosition;
private Rigidbody2D myRigidbody;
//initialize values
void Awake()
{
myRigidbody = rigidbody2D;
previousPosition = myRigidbody.transform.position;
minimumExtent = Mathf.Min(BoundsOf(collider2D).extents.x, BoundsOf(collider2D).extents.y);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector2 movementThisStep = (Vector2)myRigidbody.transform.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
//check for obstructions we might have missed
RaycastHit2D[] hitsInfo = Physics2D.RaycastAll(previousPosition, movementThisStep, movementMagnitude, layerMask.value);
//Going backward because we want to look at the first collisions first. Because we want to destroy the once that are closer to previous position
for (int i = hitsInfo.Length-1; i >= 0; i--)
{
var hitInfo = hitsInfo[i];
if (hitInfo && hitInfo.rigidbody != rigidbody2D)
{
if (Collided != null)
{
Collided(hitInfo.collider);
}
}
}
}
previousPosition = myRigidbody.transform.position;
}
// compute bounds in local space
public static Bounds BoundsOf(Collider2D collider) {
var bounds = new Bounds();
var bc = collider as BoxCollider2D;
if (bc) {
var ext = bc.size * 0.5f;
bounds.Encapsulate(new Vector3(-ext.x, -ext.y, 0f));
bounds.Encapsulate(new Vector3(ext.x, ext.y, 0f));
return bounds;
}
var cc = collider as CircleCollider2D;
if (cc) {
var r = cc.radius;
bounds.Encapsulate(new Vector3(-r, -r, 0f));
bounds.Encapsulate(new Vector3(r, r, 0f));
return bounds;
}
// others :P
//Debug.LogWarning("Unknown type "+bounds);
return bounds;
}
// return bounds in world space
public static Bounds BoundsColliders(GameObject obj) {
var bounds = new Bounds(obj.transform.position, Vector3.zero);
var colliders = obj.GetComponentsInChildren<Collider2D>();
foreach(var c in colliders) {
var blocal = BoundsOf(c);
var t = c.transform;
var max = t.TransformPoint(blocal.max);
bounds.Encapsulate(max);
var min = t.TransformPoint(blocal.min);
bounds.Encapsulate(min);
}
return bounds;
}
}
Please let me know if it works for you.
Thanks,
Lidan

Related

RayCast not working as expected, help! (Unity 3D)

Okay so I'm making a photography game where when you 'take a photo', Unity sends a few raycasts forward to check if certain tagged items are in the photo (all within the cameras FOV). My problem is, this seems to work intermittently! Sometimes it finds the tagged objects, other times it will be right in front of the view yet it will miss it completely! Can anyone advise about what I'm doing wrong?
public static Transform target;
public static GameObject[] targetName;
public static float length = 250f;
public static Transform thisObject;
// Start is called before the first frame update
void Start()
{
thisObject = GameObject.Find("Main Camera").GetComponent<Transform>();
//target = GameObject.FindGameObjectWithTag("Trees").transform;
}
// Update is called once per frame
void Update()
{
//InFront();
//HasLineOfSight("Trees");
}
public static bool InFront(Transform target1)
{
Vector3 directionToTarget = thisObject.position - target1.position;
float angleOnXAxis = Vector3.Angle(thisObject.right, directionToTarget);
float angleOnYAxis = Vector3.Angle(thisObject.up, directionToTarget);
//Debug.Log(angleOnYAxis);
if (Mathf.Abs(angleOnXAxis) < 130 && Mathf.Abs(angleOnXAxis) > 50
&& Mathf.Abs(angleOnYAxis) < 115 && Mathf.Abs(angleOnYAxis) > 62)
{
//Debug.DrawLine(transform.position, target.position, Color.green);
return true;
}
return false;
}
public static bool HasLineOfSight(string objectTag)
{
RaycastHit hit;
Vector3 direction = target.position - thisObject.position;
//Debug.Log(direction);
if (Physics.Raycast(thisObject.position, direction, out hit, length))
{
if (hit.transform.tag == objectTag)
{
Debug.DrawRay(thisObject.position, direction * 0.96f, Color.red);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public static GameObject SortObjects(string objectTag)
{
targetName = GameObject.FindGameObjectsWithTag(objectTag);
GameObject closestObject = null;
for (int i = 0; i < targetName.Length; i++)
{
if (Vector3.Distance(thisObject.position,
targetName[i].transform.position) <= length)
{
if (InFront(targetName[i].transform))
{
if (closestObject == null)
{
closestObject = targetName[i];
}
else
{
if (Vector3.Distance(targetName[i].transform.position, thisObject.position) <= Vector3.Distance(closestObject.transform.position, thisObject.position))
{
closestObject = targetName[i];
}
}
}
}
}
return closestObject;
}
public static bool ObjectCheck(string objectTag)
{
//Debug.Log(SortObjects(objectTag));
if (SortObjects(objectTag) != null)
{
target = SortObjects(objectTag).transform;
//Debug.Log(target);
if (InFront(target) && HasLineOfSight(objectTag))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
I'm essentially calling ObjectCheck() with the tag I want to check for to get the closest, visible, object with that tag. What is wrong with this code??
In your script, only the closest object to the main camera gets checked. SortObjects() determines the closest tagged object, and then you only handle that single object in ObjectCheck(). - That object might be obstructed by something else, so the method returns false. And other tagged objects that are actually visible, are not picked up this way...
So, you could rename and change your SortObjects() function to check for both conditions right in the loop (InFront(target) && HasLineOfSight(objectTag)), and filter the objects out right in there, since only those objects are of interest.
Also, your HasLineOfSight() method checks the tag of the hit object, but what you probably wanted to do, is to check if the raycast actually hits that exact object. So it should instead compare the hit's gameObject to the target's gameObject, ignoring the tag, since a correct tag alone isn't enough. (Side note: it would make sense to place all "photographable objects" on a "photo layer", and set the layer mask in the Physics.Raycast() call accordingly, it's more efficient that way in larger scenes.)
The way the angles are calculated in the InFront() method is probably causing issues, because the direction vector to the target is really in 3D. To calculate the angles, you could try to use Vector3.Project() or Vector3.ProjectOnPlane(), but that will also be problematic, because of perspective camera issues.
This check is strongly related to the topic of "frustum culling", a technique usually used for rendering. But it's similar to what you need, to filter out all the (possibly) visible objects in the camera's field of view (frustum culling doesn't handle obstruction, it is just a geometric check to see if a point lies within the camera's frustum space). See:
https://en.wikipedia.org/wiki/Viewing_frustum
https://en.wikipedia.org/wiki/Hidden-surface_determination#Viewing-
http://www.lighthouse3d.com/tutorials/view-frustum-culling/
https://docs.unity3d.com/Manual/UnderstandingFrustum.html
If you want to dig deeper and optimize this, there are a couple of ways this can be done. But luckily, Unity comes with many useful related functions already built into the Camera class. So instead, you could use Camera.WorldToScreenPoint() (or Camera.WorldToViewportPoint()), and compare the resulting screen coordinates to the screen size or viewport, like discussed in Unity forum. (The frustum math is hidden behind these compact functions, but beware that this is probably not the optimal way to do this.)
Instead of calling FindGameObjectsWithTag() every time, you could do it only once in Start(), assuming objects do not get created/destroyed while the game is running.
I've tried to modify your script, since I'm also learning Unity again... The script can be dragged to the main camera, and it should show the "focus object" in the Scene view with the green debug line. I hope this helps:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class PhotoCast : MonoBehaviour
{
public float maxDistance = 250.0f;
public string objectTag = "photo";
protected GameObject[] objs;
protected GameObject objFocus;
protected Camera cam;
public void Start() {
objs = GameObject.FindGameObjectsWithTag(objectTag);
cam = GetComponent<Camera>();
}
public void Update() {
if (Input.GetButtonDown("Fire1")) {
objFocus = CheckObjects();
if (objFocus) {
Debug.Log("closest object in view: " + objFocus.name);
/* TODO: take actual photo here */
}
}
if (objFocus) {
Debug.DrawLine(transform.position,
objFocus.transform.position, Color.green);
}
}
GameObject CheckObjects() {
GameObject obj_closest = null;
float dist_closest = float.MaxValue;
foreach (GameObject o in objs) {
float dist = Vector3.Distance(
o.transform.position, transform.position);
if (dist < maxDistance && dist < dist_closest
&& InViewport(o.transform.position)
&& HasLineOfSight(o.transform)) {
dist_closest = dist;
obj_closest = o;
}
}
return obj_closest;
}
bool InViewport(Vector3 worldPos) {
Vector3 p = cam.WorldToViewportPoint(worldPos);
return (p.x > 0.0f && p.x <= 1.0f && p.y > 0.0f && p.y <= 1.0f
&& p.z > cam.nearClipPlane);
}
bool HasLineOfSight(Transform target) {
RaycastHit hit;
Vector3 dir = target.position - transform.position;
if (Physics.Raycast(transform.position, dir, out hit, maxDistance)) {
if (hit.collider.gameObject == target.gameObject) {
return true;
}
}
return false;
}
}
Side notes:
Another issue with this technique is, that there can be tagged objects right in front of the camera, but other tagged objects that are closer on the side will be picked up instead of the obvious one. Many small issues to fine-tune until the scripts fits the game, I guess. Instead of only using one Raycast per object, you could use multiple ones, and take the bounding box or the actual collider shape into account.
An improved version of the script could make use of the Physics.Overlap*() or Physics.*Cast*() functions, documented here.

Changing the Speed of a motor in Processing/box2D

I've been making some simple games using processing and box2D using The Nature of Code as a resource.
My problem is I have gotten to a point where I have these windmills that go clockwise/counterclockwise depending on the speed of the motor (I am using PI/2 and -PI*2). I want to have it so that the user can change this speed from positive and negative by pressing a key or mouse button. Looking around online people and the box2D documentation are saying to use the function void SetMotorSpeed(float speed);, however I am not having luck figuring out how to implement this. I've tried a few ways I can think but no luck.
Currently I have this in my main file ("s" is the name of the instance of the windmill):
// Click the mouse to switch speed of motor
void mousePressed() {
s.SetMotorSpeed(s.speed);
}
And I have this in the file for the windmill:
//Set Motor Speed
void SetMotorSpeed(float speed){
speed = speed * -1;
}
This doesn't work though.
I'm fairly new to coding and this is my first post on stack-overflow so my apologies if I have done anything wrong in how I'm asking or presenting this question. I'm open to suggestions both code and etiquette wise!
Here is the code for the entire windmill:
class Seesaw {
// object is two boxes and one joint
RevoluteJoint joint;
// float speed = PI*2;
Box box1;
Box box2;
float speed = PI*2;
Seesaw(float x, float y) {
// Initialize locations of two boxes
box1 = new Box(x, y-20, 120, 10, false);
box2 = new Box(x, y, 10, 40, true);
// Define joint as between two bodies
RevoluteJointDef rjd = new RevoluteJointDef();
Vec2 offset = box2d.vectorPixelsToWorld(new Vec2(0, 60));
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
// Turning on a motor (optional)
rjd.motorSpeed = PI*2; // how fast?
rjd.maxMotorTorque = 1000.0; // how powerful?
rjd.enableMotor = true; // is it on?
// Create joint
joint = (RevoluteJoint) box2d.world.createJoint(rjd);
}
// Turn the motor on or off
void toggleMotor() {
joint.enableMotor(!joint.isMotorEnabled());
}
boolean motorOn() {
return joint.isMotorEnabled();
}
void SetMotorSpeed(float speed){
speed = -speed;
}
void display() {
box2.display();
box1.display();
// Draw anchor just for debug
Vec2 anchor = box2d.coordWorldToPixels(box1.body.getWorldCenter());
fill(255, 0, 0);
stroke(0);
ellipse(anchor.x, anchor.y, 4, 4);
}
}
The change in speed should be communicated to the joint. Try this:
void SetMotorSpeed(float speed) {
s.joint.setMotorSpeed(speed); // edited, processing/java uses camel-case
}
you might also be a bit more careful about naming variables. In your original post you use the same name for the local variable and the member variable, which has not the wanted effect. Most people use some naming convention for member variable to avoid this kind of very common error. Like for instance have all member variables all start with "_"
void SetMotorSpeed(float speed) {
_speed = -speed; // speed is local!
}

Position and rotate animations in Libgdx

I have 20 spites, I want them to be animated when a button is clicked.
Two types of animations 1) Position 2) Rotation.
Is there a recommended way to do this? Only way I can think of is recursively call setposition and angle with a delta value on Render method till the desired position and angle are reached.
When you have a start state and an end state, and you want to fill in the middle states, this is known as 'tweening (from inbetween). It comes from cartoon animation, but has come to be used more generally.
LibGDX makes use of Universal Tween Engine. You can start your journey to animating anything you want here. But, to give a bit more detail on how it works, here is an example from some of my own stuff. A similar usecase with regards to a sprite, but I have my sprites wrapped in a more generic class, a JJRenderNode. Here is how I make my class open to being tweened.
First you need a TweenAccessor for the class you want to tween.
public class RenderNodeTweenAccessor implements TweenAccessor<JJRenderNode> {
public static final int WIDTH = 1;
public static final int HEIGHT = 2;
public static final int WIDTH_HEIGHT = 3;
public static final int ALPHA = 4;
public static final int ALPHA_WIDTH_HEIGHT=5;
#Override
public int getValues(JJRenderNode target, int tweenType, float[] returnValues) {
switch (tweenType) {
case WIDTH:
returnValues[0] = target.getWidth();
return 1;
case HEIGHT:
returnValues[0] = target.getHeight();
return 1;
case WIDTH_HEIGHT:
returnValues[0] = target.getWidth();
returnValues[1] = target.getHeight();
return 2;
case ALPHA:
returnValues[0] = target.getColour().a;
return 1;
case ALPHA_WIDTH_HEIGHT:
returnValues[0] = target.getColour().a;
returnValues[1] = target.getWidth();
returnValues[2] = target.getHeight();
return 3;
default:
assert false;
return -1;
}
}
#Override
public void setValues(JJRenderNode target, int tweenType, float[] newValues) {
switch (tweenType) {
case WIDTH:
target.setWidth(newValues[0]);
break;
case HEIGHT:
target.setHeight(newValues[0]);
break;
case WIDTH_HEIGHT:
target.setWidth(newValues[0]);
target.setHeight(newValues[1]);
break;
case ALPHA:
target.getColour().a=newValues[0];
break;
case ALPHA_WIDTH_HEIGHT:
target.getColour().a=newValues[0];
target.setWidth(newValues[1]);
target.setHeight(newValues[2]);
default:
break;
}
}
}
The constant ints and the 'tweenType' in each of the get and set methods let you tween more than one combination of fields. In this case I have different combinations of width, height and alpha values for my JJRenderNode.
You have to register this TweenAccessor as follows:
Tween.registerAccessor(JJRenderNode.class, new RenderNodeTweenAccessor());
And then you are free to tween your class, for example:
Timeline.createSequence()
.push(Tween.set(node, RenderNodeTweenAccessor.WIDTH_HEIGHT).target(START_WIDTH, START_WIDTH))
.push(Tween.to(node, RenderNodeTweenAccessor.WIDTH_HEIGHT, 0.4f).target(PuzzleBlockCore.MAX_RENDER_WIDTH, PuzzleBlockCore.MAX_RENDER_WIDTH))
.start(JJ.tweenManager);
PS. You also need an instance of a TweenManger, and this needs to be updated with delta for each gameloop. I have a 'singleton' global instance that I use everywhere (JJ.TweenManager).
You might as well use spine 2d for animation. It costs, but it is worth it. For $60 I think, you get full libgdx support + bone rigging and animation.
http://esotericsoftware.com/
If you do want to animate with only libgdx however, you can create a list with all your sprite animations frames, loop through, and switch sprite texture to next frame in the animation.
private void render() {
sprite.set(aninationframes.get(currentFrame)
currentFrame = currentFrame + 1}
Though, you may want to add a delay per frame.
If(current time - time > some value) {
sprite.set(animationframes.get(currrntFrame)
currentFrame = currentFrame + 1
time = get current time here
}

Scale UI for multiple resolutions/different devices

I have a quite simple unity GUI that has the following scheme :
Where Brekt and so are buttons.
The GUI works just fine on PC and is on screen space : overlay so it is supposed to be adapted automatically to fit every screen.
But on tablet the whole GUI is smaller and reduced in the center of the screen, with huge margins around the elements (can't join a screenshot now)
What is the way to fix that? Is it something in player settings or in project settings?
Automatically scaling the UI requires using combination of anchor,pivot point of RecTransform and the Canvas Scaler component. It is hard to understand it without images or videos. It is very important that you thoroughly understand how to do this and Unity provided full video tutorial for this.You can watch it here.
Also, when using scrollbar, scrollview and other similar UI controls, the ContentSizeFitter component is also used to make sure they fit in that layout.
There is a problem with MovementRange. We must scale this value too.
I did it so:
public int MovementRange = 100;
public AxisOption axesToUse = AxisOption.Both; // The options for the axes that the still will use
public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input
public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input
private int _MovementRange = 100;
Vector3 m_StartPos;
bool m_UseX; // Toggle for using the x axis
bool m_UseY; // Toggle for using the Y axis
CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input
CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input
void OnEnable()
{
CreateVirtualAxes();
}
void Start()
{
m_StartPos = transform.position;
Canvas c = GetComponentInParent<Canvas>();
_MovementRange = (int)(MovementRange * c.scaleFactor);
Debug.Log("Range:"+ _MovementRange);
}
void UpdateVirtualAxes(Vector3 value)
{
var delta = m_StartPos - value;
delta.y = -delta.y;
delta /= _MovementRange;
if (m_UseX)
{
m_HorizontalVirtualAxis.Update(-delta.x);
}
if (m_UseY)
{
m_VerticalVirtualAxis.Update(delta.y);
}
}
void CreateVirtualAxes()
{
// set axes to use
m_UseX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal);
m_UseY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical);
// create new axes based on axes to use
if (m_UseX)
{
m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName);
CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis);
}
if (m_UseY)
{
m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName);
CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis);
}
}
public void OnDrag(PointerEventData data)
{
Vector3 newPos = Vector3.zero;
if (m_UseX)
{
int delta = (int)(data.position.x - m_StartPos.x);
delta = Mathf.Clamp(delta, -_MovementRange, _MovementRange);
newPos.x = delta;
}
if (m_UseY)
{
int delta = (int)(data.position.y - m_StartPos.y);
delta = Mathf.Clamp(delta, -_MovementRange, _MovementRange);
newPos.y = delta;
}
transform.position = new Vector3(m_StartPos.x + newPos.x, m_StartPos.y + newPos.y, m_StartPos.z + newPos.z);
UpdateVirtualAxes(transform.position);
}

How to detect a collision between one object and multiple objects in XNA 4.0 C#?

I am new to XNA and CSharp programming so I want to learn to make a treasure hunting game as a beginning so I made a player(as a class) which can walk up, down, left and right. I made a Gem class also which the player can collide with and the gem disappears and a sound is played. But I want to make some walls that the player can collide with and stop so I made a class called Tile.cs (The wall class) and I made a void in it
public void CollideCheck(bool tWalk, bool bottomWalk, bool leftWalk, bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect)
{
colRect = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
if (this.colRect.Intersects(topRect))
{
tWalk = false;
}
else
tWalk = true;
if (this.colRect.Intersects(bottomRect))
{
bottomWalk = false;
}
else
bottomWalk = true;
if (this.colRect.Intersects(leftRect))
{
leftWalk = false;
}
else
leftWalk = true;
if (this.colRect.Intersects(rightRect))
{
rightWalk = false;
}
else
rightWalk = true;
}
Then, in the Game1.cs (The main Class) I made an array of "Tiles":
Tile[] tiles = new Tile[5];
And in the update void I made this:
foreach (Tile tile in tiles)
{
tile.CollideCheck(player.topWalk, player.bottomWalk, player.leftWalk, player.rightWalk,
new Rectangle((int)player.Position.X, (int)player.Position.Y - (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X, (int)player.Position.Y + (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X + (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X - (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight));
}
All those rectangles are the borders of the player but when I run the game the player doesn't collide with it so is there any way to fix this?
I can post the project if I am not very clear.
Your parameters are in only, but you set their values inside the call. You have to declare them as out variables so that their value is sent back to the caller. Using out also makes sure you always set a value to them before exiting the function.
So change your function declaration to public void CollideCheck(out bool tWalk, out bool bottomWalk, out bool leftWalk, out bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect) and you get the values back.

Resources