Changing the Speed of a motor in Processing/box2D - processing

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!
}

Related

Networking rotation sync

My Unity version is 5.2.3f1, I m trying to sync the rotation of a child gameobject, in local works perfectly fine but it doesnt show up in other clients. I tried everything I could find and nothing.
The reason of this is to rotate a FPS body, so, I m trying to rotate Spine2 (Rotate the camera is not my best solution). I m using a Mixamo character to test, in the end I will have Mixamo auto-rigged charscters so everything I make here will be compatible.
I tried to use the Network Transform Rigidbody 3D and it only sync the character itself, not Spine2, I have tried Network Transform Child, and an official skeleton sync.
In the script part, I have tried a lot of things, the most promising one was this:
[SyncVar]
private Quaternion syncPlayerRotation;
[SerializeField]
private Transform playerTransform;
[SerializeField]
private float lerpRate = 15f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void LateUpdate () {
TransmitRotations();
LerpRotations();
}
void LerpRotations()
{
if (!isLocalPlayer)
playerTransform.localRotation = Quaternion.Lerp(playerTransform.localRotation, syncPlayerRotation, Time.deltaTime * lerpRate);
}
[Command]
void CmdProvideRotationsToServer(Quaternion playerRot)
{
syncPlayerRotation = playerRot;
}
[Client]
void TransmitRotations()
{
if (isLocalPlayer)
{
CmdProvideRotationsToServer(playerTransform.localRotation);
}
}
Its from UNET tutorial series on youtube, Gamer To Game Developer user.
I attached it to Spine2 and still dont work, but when I attached it to the main character, it worked.
Also tried this:
void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
Vector3 syncPosition = Vector3.zero;
if (stream.isWriting)
{
syncPosition = Spine.GetComponent<Rigidbody>().position;
stream.Serialize(ref syncPosition);
}
else
{
stream.Serialize(ref syncPosition);
Spine.GetComponent<Rigidbody>().position = syncPosition;
}
}
But I think it was for an older version of Unity.
To make the rotations I m using A Free Simple Smooth Mouselook
I edited it, this lines:
if (Input.GetMouseButton(1))
{
var xRotation = Quaternion.AngleAxis(-_mouseAbsolute.y, targetOrientation * Vector3.forward);
transform.localRotation = xRotation;
}
else
{
var xRotation = Quaternion.AngleAxis(-_mouseAbsolute.y, targetOrientation * Vector3.right);
transform.localRotation = xRotation;
}
Basicly, I changed Vector3.right to Vector3.forward and converted the Vector3.right only if the right mouse button is not pressed. The script is attached to Spine2 and its activated on the start if(isLocalPlayer) by script.
There's a pic of the current hierarchy:
(some cameras are there only to test, the main camera is FirstPersonCamera, extracted from the standard assets)
I noticed that if I debug log the Spine2 rotation, it only gives me values from 0 to 1.

Fast moving objects 2D game (Unity3d)

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

Animate/move/translate/tween image in Unity 4.6 from C# code

How can I move/animate/translate/tween an Image from position A to position B using C# code in Unity 4.6?
Assuming Image is a GameObject, so it could be a Button or whatever.
There has to be a one-liner for this, right? I've been googling for a while but all I can see out-of-the-box is stuff done in Update, and I firmly believe doing stuff in Update is not a fast way of scripting things.
maZZZu's method will work, however, if you do NOT want to use the Update function, you can use an IEnumerator/Coroutine like so…
//Target object that we want to move to
public Transform target;
//Time you want it to take before it reaches the object
public float moveDuration = 1.0f;
void Start ()
{
//Start a coroutine (needed to call a method that returns an IEnumerator
StartCoroutine (Tween (target.position));
}
//IEnumerator return method that takes in the targets position
IEnumerator Tween (Vector3 targetPosition)
{
//Obtain the previous position (original position) of the gameobject this script is attached to
Vector3 previousPosition = gameObject.transform.position;
//Create a time variable
float time = 0.0f;
do
{
//Add the deltaTime to the time variable
time += Time.deltaTime;
//Lerp the gameobject's position that this script is attached to. Lerp takes in the original position, target position and the time to execute it in
gameObject.transform.position = Vector3.Lerp (previousPosition, targetPosition, time / moveDuration);
yield return 0;
//Do the Lerp function while to time is less than the move duration.
} while (time < moveDuration);
}
This script will need to be attached to the GameObject that you would like to move. You will then need to create another GameObject in the scene that will be your Target…
The code is commented but if you need clarification on something just post a comment here.
If you want to do the movement yourself you can use something like this:
public Vector3 targetPosition = new Vector3(100, 0, 0);
public float speed = 10.0f;
public float threshold = 0.5f;
void Update () {
Vector3 direction = targetPosition - transform.position;
if(direction.magnitude > threshold){
direction.Normalize();
transform.position = transform.position + direction * speed * Time.deltaTime;
}else{
// Without this game object jumps around target and never settles
transform.position = targetPosition;
}
}
Or you can download for example DOTween package and just start the tween:
public Vector3 targetPosition = new Vector3(100, 0, 0);
public float tweenTime = 10.0f;
void Start () {
DOTween.Init(false, false, LogBehaviour.Default);
transform.DOMove(targetPosition, tweenTime);
}

Working Fine In Java Mode, Not So In Javascript (Also Trouble With Algorithm)

I'm currently teaching myself game programming and remaking some of the classic games to learn the different tricks and stuff(as suggested a fine article on gamedev.net).
I successfully coded PONG and now working on Snake(I assume that's what it meant by Worm). Now I figured out a lot of the stuff, except for two things I can't wrap my mind around.
My algorithm is simple enough: The player controls the head and the body follows. The head has it's own class and from there each segment is controlled as a separate object. The segments each control their own movement defined by a velocity vector. The first segment is independent from the array that controls the rest of the segments. So the head just sends orders to the first segment and the first segment transmits it to the rest of the segments.
The system is based on BendinPoints. Basically each segment has a variable for storing a BendingPoint coordinate and the velocity vector to take on when reaching that BendingPoint. A flag tells it whether it currently posses a BendingPoint or is free to accept a new coordinate.
So the head, when it turns, transmits that location where it turned and the direction(velocity vector) in which it turned. The first segment passes it on the second, the second passes it on to the third and so on. Each segment only passes an order to the next if it has one to pass. Each segment only receives new orders once the old one has been completed.
Now my problem is two-fold. One: this works fine in Java mode but not working in Javascript mode and I can't figure out why. And Two: Sometimes, when I'm changing direction too rapidly, the rest of the body apart from the head and first segment seem to lose track and wander off.
I hope the code comments will explain the rest. Forgive my newbishness.
String MODE;
Menu menu;
String[] menuItems={"START","INSTRUCTIONS","CREDITS","EXIT"};
/*# pjs font="data/waved.ttf" */
/*# pjs font="data/sixty.ttf" */
PFont sMenu=createFont("waved",72);
PFont sItem=createFont("sixty",35);
String gOverText="GAME OVER";
String hScoreText="Your score is: ";
String iControl="W,A,S,D turns the Snake in the respective direction.";
String iScore="Each Food increases Score by 1 and a segement is added.";
String iScore2="After every 10 points, number of segments added per Food increases by 1.";
String cBy="coded By";
String cName="Le Shaun";
MenuItem back;
Snake snk;
Food fd;
int hScore;
int dF;
float sWidth=800;
float sHeight=600;
PVector sLoc=new PVector(sWidth/2,sHeight/2);
PVector sVel=new PVector(0,-1);
float sRad=10;
color sCol=#9D6C0A;
PVector fLoc=new PVector(450,300);
float fRad=10;
color fCol=#FCF18C;
void setup(){
size(int(sWidth),int(sHeight));
snk=new Snake(sLoc,sVel,sRad,sCol);
fd=new Food(fLoc,fRad,fCol);
frameRate(60);
hScore=0;
dF=1;
menu=new Menu("SNAKE",menuItems,sMenu,sItem,color(#9D6C0A),color(#8CC610),color(#EDE724),color(#674707),color(255,0));
MODE="NIL";
back=new MenuItem("BACK",sItem,width/2,height/1.5,height/25,color(#8CC610),color(#EDE724),color(#674707),color(255,0)); //Common back button for some of the screens.
}
//Current screen is controlled by MODES. Each MODE defines which parts of the game will run, whether it be individual screens or the main gameplay itself.
void draw(){
background(#EDB824);
if(MODE.equals("NIL")){
menu.render();
MODE=menu.whichItem();
}
else if(MODE.equals("START")){
fd.render();
if(fd.isEaten(snk)){
for(int i=1;i<=dF;i++){
snk.sInc();
}
hScore++;
}
snk.render();
snk.update();
if(snk.isDead()){
MODE="GAMEOVER";
sLoc=new PVector(width/2,height/2);
fLoc=new PVector(width/2+100,height/2+100);
sVel=new PVector(+1,0);
snk=new Snake(sLoc,sVel,sRad,sCol);
}
dF=int(hScore/10)+1;
textFont(sItem);
textSize(height/25);
text(str(hScore),width-textWidth(str(hScore))*3,height-height/25);
}
else if(MODE.equals("GAMEOVER")){
stroke(0);
fill(#9D6C0A);
textFont(sMenu);
textSize(72);
text(gOverText,width/2-textWidth(gOverText)/2,height/3);
text(hScoreText+hScore,width/2-textWidth(gOverText)/2,height/2);
back.render();
back.update();
if(back.getClicked()){
back.unClick();
MODE="NIL";
hScore=0;
frameRate(60);
}
}
else if(MODE.equals("INSTRUCTIONS")){
stroke(0);
fill(#9D6C0A);
textFont(sMenu);
textSize(72);
text("INSTRUCTIONS",width/2-textWidth("INSTRUCTIONS")/2,height/3);
textFont(sItem);
textSize(20);
text(iControl,width/2-textWidth(iControl)/2,height/2);
text(iScore,width/2-textWidth(iScore)/2,height/2+35);
text(iScore2,width/2-textWidth(iScore2)/2,height/2+70);
back.render();
back.update();
if(back.getClicked()){
back.unClick();
MODE="NIL";
}
}
else if(MODE.equals("CREDITS")){
stroke(0);
fill(#9D6C0A);
textFont(sItem);
textSize(35);
text(cBy,width/2-textWidth(cBy)/2,height/2);
textSize(45);
text(cName,width/2-textWidth(cName)/2,height/1.7);
back.render();
back.update();
if(back.getClicked()){
back.unClick();
MODE="NIL";
}
}
//println(MODE);
}
void keyReleased(){
if(MODE.equals("START")){
String temp="";
temp+=key;
temp=temp.toUpperCase();
snk.changeDir(temp);
if(key=='v' || key=='V'){
frameRate(60);
}
}
}
void keyPressed(){
if(MODE.equals("START")){
if(key=='v' || key=='V'){
frameRate(180);
}
}
}
void mouseClicked(){
if(MODE.equals("NIL")){
menu.passTo(mouseX,mouseY);
}
if(MODE.equals("GAMEOVER") || MODE.equals("INSTRUCTIONS") || MODE.equals("CREDITS")){
back.mClicked(mouseX,mouseY);
}
}
//Menu class uses the objects from the MenuItem and forms a menu with a title and a list of MenuItem objects.
/*
Constructor: Str-MenuTitle, Str[]-MenuItems, PF-MenuFont, PF-MenuItemFont, c-TitleColor, c-ItemTextColor, c-ItemBackColor, c-ItemHoverTextColor, c-ItemHoverBackColor.
Methods:
void render() - Renders the MenuTitle and the MenuItems.
void passTo(float,float) - Passes the mouse coords to each MenuItem to check whether it has been clicked.
void passTo(int) - Resets the clickState of the specified MenuItem by calling the unClick() method on that MenuItem.
String whichItem() - Checks all the MenuItems for a their clickState and returns the one that's been clicked.
*/
class Menu{
String titleT;
PFont titleF;
PFont menuItem;
color titleC;
float spacer; //This is used to define the space between successive MenuItem objects.
float iniY=height/2.5;
MenuItem[] menuItems;
Menu(String titleT,String[] menuItemsNames,PFont titleF,PFont menuItemF,color titleC,color menuItemC,color menuBackC,color itemHoverC,color backHoverC){
this.titleT=titleT;
this.titleF=titleF;
this.titleC=titleC;
menuItems=new MenuItem[menuItemsNames.length]; //Initializes the MenuItem objects depending on the array passed to it. This makes the menu system very flexible.
spacer=48;
for(int i=0;i<menuItemsNames.length;i++){
menuItems[i]=new MenuItem(menuItemsNames[i],menuItemF,width/2,iniY+(spacer*i),height/25,menuItemC,menuBackC,itemHoverC,backHoverC);
}
}
void render(){ //Renders the menu.
textFont(titleF);
textSize(92);
fill(titleC);
text(titleT,width/2-(textWidth(titleT)/2),height/3.8);
for(int i=0;i<menuItems.length;i++){
menuItems[i].update();
menuItems[i].render();
}
}
void passTo(float mX,float mY){ //This accepts the X,Y mouse coords when the mouse is clicked and passes it to the relevant MenuItem object to check if the click occurs on that object.
for(int i=0;i<menuItems.length;i++){
menuItems[i].mClicked(mX,mY);
}
}
/*void passTo(int item){ //This accepts an ineteger value and resets that particular menu item's click state.
menuItems[item].unClick();
}*/
String whichItem(){ //Checks each time if the clickState of any MenuItem object is true. If it is, returns the array position of the relevant object.
for(int i=0;i<menuItems.length;i++){
if(menuItems[i].getClicked()){
menuItems[i].unClick();
return menuItems[i].menuItem;
}
}
return "NIL";
}
}
//MenuItem holds the attributes and methods relating to each single item on the menu. Thus each item is treated as a separate object.
//Each MenuItem object comprises mainly of a foreground text and a background object.
class MenuItem{
String menuItem;
PFont menuFont;
float itemX;
float itemY;
float itemSize;
color itemColor;
color backColor;
color pressedColor;
color pressedBack;
color presentItem;
color presentBack;
float textWidth;
boolean clickState=false; //This vairable is used to check the clickState of the menu item. If the mouse is clicked over the menu item, this variable becomes true.
MenuItem(String menuItem,PFont menuFont,float itemX,float itemY,float itemSize,color itemColor,color backColor,color pressedColor,color pressedBack){
this.menuItem=menuItem;
this.menuFont=menuFont;
this.itemX=itemX;
this.itemY=itemY;
this.itemSize=itemSize;
this.itemColor=itemColor;
this.backColor=backColor;
this.pressedColor=pressedColor;
this.pressedBack=pressedBack;
}
void render(){ //Handles the rendering for individual menu objects.
textFont(menuFont);
textSize(itemSize);
textWidth=textWidth(menuItem);
stroke(0);
fill(presentBack);
rectMode(CENTER);
rect(itemX,itemY,textWidth*1.3,itemSize*1.4,50);
fill(presentItem);
text(menuItem,itemX-textWidth/2,itemY+itemSize*.3);
}
void update(){ //Constatnly checks for the state of the object. If the mouse is over it a certain style is show and otherwise another style is shown.
if(mouseX<(itemX+(textWidth*1.3)/2) && mouseX>(itemX-(textWidth*1.3)/2) && mouseY<(itemY+(itemSize*1.4)/2) && mouseY>(itemY-(itemSize*1.4)/2)){
presentItem=pressedColor;
presentBack=pressedBack;
noStroke();
}
else{
presentItem=itemColor;
presentBack=backColor;
}
}
boolean getClicked(){ //Returns the clickState of the object.
return clickState;
}
void unClick(){ //Resets the click state after having been clicked once.
clickState=false;
}
void mClicked(float mX,float mY){ //Changes the clickState of the object depending on the position of the mouse as inputs.
if(mX<(itemX+(textWidth*1.3)/2) && mX>(itemX-(textWidth*1.3)/2) && mY<(itemY+(itemSize*1.4)/2) && mY>(itemY-(itemSize*1.4)/2)){
clickState=true;
println(menuItem);
}
}
}
/*
All control comes from the Snake's head. The head works directly with the first segment(SnakeBits object) and the first segement works with the rest of the body.
Each time a food is consumed, a new segment is created, it's position and velocity calculated as per the position of the last segment.
A loop checks whether each segment is open to receiving a new set of orders(BendingPoint and the velocity for that point), and passes on if so.
*/
class Snake{ //Controls the snake's head as well as the segment objects.
PVector sLoc; //Location and Velocity.
PVector sVel;
float sRad; //Radius and Color
float shRad;
color sCol;
float baseVel; //The base velocity of the snake.
SnakeBits[] sBits={}; //Array of SnakeBits objects that forms the segments.
PVector hold;
Snake(PVector sLoc,PVector sVel,float sRad,color sCol){
this.sLoc=sLoc;
this.sVel=sVel;
this.sRad=sRad;
this.shRad=sRad*1.;
this.sCol=sCol;
this.baseVel=abs(sVel.x>0 ? sVel.x : sVel.y); //The snake is initially given a vector in one of the cardinal directions. Whatever the value of velocity is in either direction is stored.
hold=PVector.mult(sVel,shRad+sRad);
hold=PVector.sub(sLoc,hold);
sBits=(SnakeBits[])append(sBits,new SnakeBits(hold,sVel,sRad,sCol));
}
void update(){ //Updates the movement of the head as well as the segments.
updateBP();
sLoc.add(sVel);
for(int i=0;i<sBits.length;i++){
sBits[i].update();
}
}
void render(){ //The display.
stroke(0);
fill(sCol);
ellipse(sLoc.x,sLoc.y,shRad*2.2,shRad*2.2);
for(int i=0;i<sBits.length;i++){
sBits[i].render();
}
}
void sInc(){ //Gets called each time a food item is eaten, and increases the size of the snake by adding segments based on the velocity vector of the last segment.
int lastInd=sBits.length-1;
hold=PVector.mult(sBits[lastInd].sbVel,sRad*2);
hold=PVector.sub(sBits[lastInd].sbLoc,hold);
PVector appVel=new PVector(sBits[lastInd].sbVel.x,sBits[lastInd].sbVel.y);
SnakeBits appBits=new SnakeBits(hold,appVel,sRad,sCol);
sBits=(SnakeBits[])append(sBits,appBits);
}
void changeDir(String dir){ //Gets called when a directional button is pressed.
PVector chng=new PVector(0,0); //Direction change can only occur perpendicular to the current direction. Uses baseVel to set the new direction.
if(!sBits[0].hasBP){
if(degrees(sVel.heading())==0 || degrees(sVel.heading())==180){
if(dir.equals("W")){
chng=new PVector(0,-baseVel);
sVel=chng;
updateFBP();
}
else if(dir.equals("S")){
chng=new PVector(0,baseVel);
sVel=chng;
updateFBP();
}
}
else if(degrees(sVel.heading())==90 || degrees(sVel.heading())==-90){
if(dir.equals("D")){
chng=new PVector(baseVel,0);
sVel=chng;
updateFBP();
}
else if(dir.equals("A")){
chng=new PVector(-baseVel,0);
sVel=chng;
updateFBP();
}
}
}
}
boolean isDead(){ //Checks for collision against the wall or it's own tail.
if((sLoc.x-shRad)<0 || (sLoc.x+shRad)>width || (sLoc.y-shRad)<0 || (sLoc.y+shRad)>height){
println("WALL");
return true;
}
PVector temp;
for(int i=0;i<sBits.length;i++){
if(dist(sLoc.x,sLoc.y,sBits[i].sbLoc.x,sBits[i].sbLoc.y)<(shRad+sRad-sRad*.6)){
println("TAIL");
println(sLoc.x+" "+sLoc.y+" "+sBits[i].sbLoc.x+" "+sBits[i].sbLoc.y+" "+dist(sLoc.x,sLoc.y,sBits[i].sbLoc.x,sBits[i].sbLoc.y)+" "+(shRad+sRad-sRad*.6));
return true;
}
}
return false;
}
void updateFBP(){ //Updates the first segment's BendingPoint.
sBits[0].takeNewBP(sLoc,sVel);
sBits[0].hasNewBP(true);
}
void updateBP(){ //Updates the rest of the segments as per the system of receiving new orders once the current orders have been executed.
for(int i=0;i<sBits.length-1;i++){
if(sBits[i].hasBP && !sBits[i+1].hasBP){
sBits[i+1].takeNewBP(sBits[i].newBP,sBits[i].newVel);
sBits[i+1].hasNewBP(true);
}
}
}
}
/*
Each SnakeBit has it's independent movement system. It holds a BendPoint(newBP) variable, a New BP Velocity(newVel) variable and a flag(hasBP) to show whether it has a new Bend Point.
When the SnakeBit already has a BP, it will wait till it reaches that BP and then take on the velocity from newVel. It's flag will be set to false.
In this state it will be open to receiving a new set of orders: a new BP and the velocity to take on for that BP. Thus new BP's are not taken on till the previous BP has been cleared.
*/
class SnakeBits{ //The individual bits of the snake that make up its body.
boolean hasBP;
PVector sbLoc; //Location and Velocity vectors.
PVector sbVel;
float sbRad; //Radius and color of the segment.
color sbCol;
PVector newBP; //This works with the changeDir() method. It holds the position at which the direction will be changed.
PVector newVel; //Stores the new Velocity vector that will be applied when the above position is reached.
SnakeBits(PVector sbLoc,PVector sbVel,float sbRad,color sbCol){
this.sbLoc=sbLoc;
this.sbVel=sbVel;
this.sbRad=sbRad;
this.sbCol=sbCol;
newVel=new PVector(sbVel.x,sbVel.y);
newBP=new PVector(width*2,height*2); //Initialized it as such to avoid problems during first run.
hasBP=false;
}
void render(){
stroke(0);
fill(sbCol);
ellipse(sbLoc.x,sbLoc.y,sbRad*2,sbRad*2);
}
void update(){
sbLoc.add(sbVel); //Both updates the Location and checks if it's time to change direction.
changeDir();
}
void changeDir(){
if(sbLoc.x==newBP.x && sbLoc.y==newBP.y && hasBP){ //As soon as the segment reaches the Location where a change in dir is needed, the Velocity is changed over to the new velocity vector.
println("FTRUE");
hasNewBP(false);
sbVel.x=newVel.x; sbVel.y=newVel.y;
newBP=new PVector(width*2,height*2);
}
}
void takeNewBP(PVector pos,PVector vel){ //Called externally by the Snake class. Takes where last segment changed direction and stores that location as well as the new velocity vector.
newBP.x=pos.x; newBP.y=pos.y;
newVel.x=vel.x; newVel.y=vel.y;
}
void hasNewBP(boolean dat){ //Updates the hasBP state by accepting a boolean and assigning it to hasBP.
hasBP=dat;
}
}
class Food{
PVector fLoc;
float fRad;
color fCol;
Food(PVector fLoc,float fRad,color fCol){
this.fLoc=fLoc;
this.fRad=fRad;
this.fCol=fCol;
}
void render(){
stroke(0);
fill(fCol);
ellipse(fLoc.x,fLoc.y,fRad*2,fRad*2);
}
boolean isEaten(Snake sn){
PVector temp;
temp=PVector.sub(fLoc,sn.sLoc);
if(temp.mag()<(sn.shRad+fRad)){
reset(sn);
return true;
}
return false;
}
void reset(Snake sn){
boolean set=false;
PVector tmp=new PVector();
while(!set){
tmp=new PVector(random(fRad,width-fRad),random(fRad,height-fRad));
set=true;
for(int i=0;i<sn.sBits.length;i++){
if(dist(tmp.x,tmp.y,sn.sBits[i].sbLoc.x,sn.sBits[i].sbLoc.y)<(fRad+sn.sRad) || dist(tmp.x,tmp.y,sn.sLoc.x,sn.sLoc.y)<(fRad+sn.shRad)){
set=false;
break;
}
}
}
fLoc=tmp;
}
}
Javascript mode seems to have a problem with textWidth(); To get over it you can change textWidth=textWidth(menuItem); in MenuItem.render() to textWidth=200; or find a working equivalent.
By the way, I noticed that when I run it in Javascript, it wouldn't take its full size and your size(); command is the culprit. It seems to require numbers instead of variables to function properly (setting it to size(800,600); worked)
Your other problem seems to stem from the fact that you change things when you press a button but you dont apply the change until the draw() function. Unfortunately I can't pinpoint it in your code by making small changes, which makes me believe that you need to refactor your turning system in a different fashion. My suggestion is to decouple where everyone has to go from where the previous one goes, opting instead to store the positions and directions he has to go in an ArrayList, which will grow essentially with each turn. Then, each bit of the snake has to go through its own ArrayList of directions and positions without regarding the momentary changes of its previous one. When it does reach one of those positions just turn it in the proper direction, delete the reached direction and position and start heading for the next one. The ArrayList essentially functions like a queue, where you add to the end but remove from the beginning.
Maybe it makes sense to post the second part of your question to https://gamedev.stackexchange.com/

Rotation in Unity3D

This is a simplified code from what I'm trying to do:
var angle = 1.57;
if ( this.transform.rotation.y > angle ){
this.transform.rotation.y--;
} else if ( this.transform.rotation.y < angle ){
this.transform.rotation.y++;
}
I'm used to code in AS3, and if I do that in flash, it works perfectly, though in Unity3D it doesn't, and I'm having a hard time figuring out why, or how could I get that effect.
Can anybody help me? Thanks!
edit:
my object is a rigidbody car with 2 capsule colliders driving in a "bumpy" floor, and at some point he just loses direction precision, and I think its because of it's heirarchical rotation system.
(thanks to kay for the transform.eulerAngles tip)
transform.rotation retrieves a Quaternion. Try transform.rotation.eulerAngles.y instead.
Transform Rotation is used for setting an angle, not turning an object, so you would need to get the rotation, add your change, and then set the new rotation.
Try using transform.rotate instead.
Check the Unity3d scripting reference here:
http://unity3d.com/support/documentation/ScriptReference/Transform.Rotate.html
I see two problems so far. First the hierarchical rotation of Unity. Based on what you are trying to achieve you should manipulate either
transform.localEulerAngles
or
transform.eulerAngles
The second thing is, you can't modify the euler angles this way, as the Vectors are all passed by value:
transform.localEulerAngles.y--;
You have to do it this way:
Vector3 rotation = transform.localEulerAngles;
rotation.y--;
transform.localEulerAngles = rotation;
You need to create a new Quaternion Object
transform.rotation = Quaternion.Euler ( transform.rotation.x, transform.rotation.y++, transform.rotation.z );
You can also use transform.Rotate function.
The above suggestion to use transform.Rotate( ) is probably what you're going to need to do to actually make it rotate, BUT the variables of transform.Rotate( ) are velocity/speed rather than direction, so transform.Rotate( ) will have to use more than one axis if you want an angled rotation. Ex:
class Unity // Example is in C#
{
void Update( )
{
gameObject.transform.Rotate(0, 1, 0);
}
}
This will rotate the object around its y-axis at a speed of 1.
Let me know if this helps - and if it hinders I can explain it differently.
You should try multiplyng your rotation factor with Time.deltaTime
Hope that helps
Peace
Here is my script for GameObject rotation with touch
//
// RotateController.cs
//
// Created by Ramdhan Choudhary on 12/05/13.
//
using UnityEngine;
using System;
public class RotateController
{
private float RotationSpeed = 9.5f;
private const float mFingerDistanceEpsilon = 1.0f;
public float MinDist = 2.0f;
public float MaxDist = 50.0f;
private Transform mMoveObject = null;
private bool isEnabledMoving = false;
//************** Rotation Controller Constructor **************//
public RotateController (Transform goMove)
{
isEnabledMoving = true;
mMoveObject = goMove;
if (mMoveObject == null) {
Debug.LogWarning ("Error! Cannot find object!");
return;
}
}
//************** Handle Object Rotation **************//
public void Update ()
{
if (!isEnabledMoving && mMoveObject != null) {
return;
}
Vector3 camDir = Camera.main.transform.forward;
Vector3 camLeft = Vector3.Cross (camDir, Vector3.down);
// rotate
if (Input.touchCount == 1) {
mMoveObject.Rotate (camLeft, Input.touches [0].deltaPosition.y * RotationSpeed * Time.deltaTime, Space.World);
mMoveObject.Rotate (Vector3.down, Input.touches [0].deltaPosition.x * RotationSpeed * Time.deltaTime, Space.Self);
}
}
}

Resources