I want to use the MouseJoint in Java for Android. I am new in box2d and cocos2d. I don't know how to use the mouse joint.
i recomend you to see this tutorial example. Copy from it (sorry i don't like broken links :) ) ...
you drag your body with the help of the MouseJoint, it will collide with the other bodys in the world and apply force to them.
Box2d - Manual - http://www.box2d.org/manual.html#_Toc258082974
8.10 Mouse Joint
The mouse joint is used in the testbed to manipulate bodies with the mouse. It attempts to drive a point on a body towards the current position of the cursor. There is no restriction on rotation.
The mouse joint definition has a target point, maximum force, frequency, and damping ratio. The target point initially coincides with the body’s anchor point. The maximum force is used to prevent violent reactions when multiple dynamic bodies interact. You can make this as large as you like. The frequency and damping ratio are used to create a spring/damper effect similar to the distance joint.
Many users have tried to adapt the mouse joint for game play. Users often want to achieve precise positioning and instantaneous response. The mouse joint doesn’t work very well in that context. You may wish to consider using kinematic bodies instead.
So let's start..
You have to create your PhysicWorld and at least one body in it. ( Checkout the PhysicExample how to.. )
- MouseJoint method
public MouseJoint createMouseJoint(AnimatedSprite box , float x, float y)
{
final Body boxBody =
this.mPhysicsWorld.getPhysicsConnectorManager().findBodyByShape(box);
Vector2 v = boxBody.getWorldPoint(
new Vector2(x/pixelToMeteRatio, y/pixelToMeteRatio)
);
MouseJointDef mjd = new MouseJointDef();
mjd.bodyA = groundBody;
mjd.bodyB = boxBody;
mjd.dampingRatio = 0.2f;
mjd.frequencyHz = 30;
mjd.maxForce = (float) (200.0f * boxBody.getMass());
mjd.collideConnected = true;
mjd.target.set(v);
return (MouseJoint) this.mPhysicsWorld.createJoint(mjd);
}
- Touching Body
we have to override our onAreaTouched method to create an MouseJoint anchor-point on the touch position.
MouseJoint mjActive = null;
private float pixelToMeteRatio = PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT;
#Override
public boolean onAreaTouched(
final TouchEvent pSceneTouchEvent,
final ITouchArea pTouchArea ,
final float pTouchAreaLocalX,
final float pTouchAreaLocalY )
{
if(pSceneTouchEvent.getAction() == MotionEvent.ACTION_DOWN) {
this.runOnUpdateThread(new Runnable() {
#Override
public void run() {
final AnimatedSprite face = (AnimatedSprite)pTouchArea; //The touched body
//If we have a active MouseJoint, we are just moving arround don't create an 2nd one.
if( mjActive == null)
{
Vector2 vector = new Vector2(pTouchAreaLocalX/pixelToMeteRatio,pTouchAreaLocalY/pixelToMeteRatio);
//=====================================
// GROUNDBODY - Used for the MouseJoint
//=====================================
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(vector);
groundBody = mPhysicsWorld.createBody(groundBodyDef);
//====================================
// CREATE THE MOUSEJOINT
//====================================
mjActive = PhysicsJumpExample.this.createMouseJoint(face, pTouchAreaLocalX, pTouchAreaLocalY);
}
}});
return true;
}
return false;
}
- Moving the body
We are moving our finger over the scene, so we have to move the MouseJoint too.
If we release the finger.. we must destroy the MouseJoint..
#Override
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
if(this.mPhysicsWorld != null) {
if(pSceneTouchEvent.getAction() == MotionEvent.ACTION_MOVE) {
this.runOnUpdateThread(new Runnable() {
#Override
public void run() {
if( mjActive != null ){ //If the MJ is active move it ..
// =========================================
// MOVE THE MOUSEJOINT WITH THE FINGER..
// =========================================
Vecotr2 vec = new Vector2(pSceneTouchEvent.getX()/pixelToMeteRatio, pSceneTouchEvent.getY()/pixelToMeteRatio);
mjActive.setTarget(vec);
}
}});
return true;
}
//===========================================
// RELEASE THE FINGER FROM THE SCENE..
//===========================================
if( pSceneTouchEvent.getAction() == MotionEvent.ACTION_UP ||
pSceneTouchEvent.getAction() == MotionEvent.ACTION_CANCEL
) {
this.runOnUpdateThread(new Runnable() {
#Override
public void run() {
if( mjActive != null )
{
//======================================
// DESTROY OUR MOUSEJOINT
//======================================
PhysicsJumpExample.this.mPhysicsWorld.destroyJoint(mjActive);
PhysicsJumpExample.this.mPhysicsWorld.destroyBody(groundBody);
mjActive = null;
}
}});
return true;
}
return false;
}
FYI:
To fit your needs, you have to play with this settings ( in the createMouseJoint method )
mjd.dampingRatio = 0.2f;
mjd.frequencyHz = 30;
mjd.maxForce = (float) (200.0f * boxBody.getMass());
Related
I want to get an image like in the picture with code. I stacked boxes because i must add joint with code. I want to make the boxes look like this as the character slides left and right smoothly. And how can i smooth swerving control in unity for touch or mouse button?
I tried this codes for movement:
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
firstPos = touch.position;
if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
Swerving(touch.position);
}
private void Swerving(Vector2 touch)
{
endPos = touch;
float diff = endPos.x - firstPos.x;
transform.Translate(diff * Time.deltaTime * swerveSpeed, 0, 0);
}
But not smooth swerving.
I tried hinge joint for image. I tried random values to motor,spring etc. But it didnt work i have never used joints.
In general when dealing with physics you shouldn't use transform this break collision detection and other physics related things like your joints etc.
Try and rather go through the Rigidbody using e.g. Rigidbody.MovePosition (or accordingly Rigidbody2D.MovePosition if your are in 2D) within FixedUpdate like e.g.
// or Rigidbody2D depending on your needs
[SerializeField] private Rigidbody _rigidbody;
private Vector2 startPos;
private float movement;
private void Awake()
{
if(!_rigidbody) rigidbody = GetComponent<Rigidbody>();
}
// get user input in Update in general
private void Update()
{
if (Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
switch(touch.phase)
{
case TouchPhase.Began:
startPos = touch.position;
movement = 0;
break;
case TouchPhase.Stationary:
case TouchPhase.Moved:
movement = touch.position.x - startPos.x;
break;
default:
movement = 0;
}
}
}
// Apply Physics in FixedUpdate!
private void FixedUpdate()
{
_rigidbody.MovePosition(_rigidbody.position + Vector3.right * movement * Time.deltaTime * swerveSpeed);
}
However, in general the touch is a 2D pixel space position which might be quite different on different devices.
I would rather translate it into a 3D world space position and use something like e.g.
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private Camera _mainCamera;
private float _offset;
private Plane? _hitPlane;
private Vector3 _targetPosition;
private void Awake()
{
if (!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
if (!_mainCamera) _mainCamera = Camera.main;
}
// get user input in Update in general
private void Update()
{
_targetPosition = _rigidbody.position;
if (Input.touchCount <= 0)
{
return;
}
var touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Began:
{
// Check if you are touching the character
var ray = _mainCamera.ScreenPointToRay(touch.position);
if (Physics.Raycast(ray, out var hit) && hit.rigidbody == _rigidbody)
{
// create a virtual plane parallel to the camera view
// passing the hit point
_hitPlane = new Plane(-ray.direction, hit.point);
// also store the offset from pivot to the hit point
// we only care about the X axis
_offset = (_rigidbody.position - hit.point).x;
}
break;
}
case TouchPhase.Stationary:
case TouchPhase.Moved:
{
if (_hitPlane.HasValue)
{
// now instead keep ray casting against that virtual plane
var ray = _mainCamera.ScreenPointToRay(touch.position);
if (_hitPlane.Value.Raycast(ray, out var distance))
{
var hitPoint = ray.GetPoint(distance);
_targetPosition.x = hitPoint.x + _offset;
}
}
break;
}
default:
_hitPlane = null;
break;
}
}
// Apply Physics in FixedUpdate!
private void FixedUpdate()
{
_rigidbody.MovePosition(_targetPosition);
}
this makes sure your character is definitely placed on the X axis where you are dragging it without any delays but still complying with physics
so as my previous threads show, I am creating a gameObject from sprites images at runtime using this code:
tex = Resources.Load<Texture2D>("pig") as Texture2D;
Sprite sprite = new Sprite();
sprite = Sprite.Create(tex, new Rect(0, 0, 250, 150), new Vector2(0.5f, 0.5f));
GameObject newSprite = new GameObject();
newSprite.AddComponent<Rigidbody2D>();
newSprite.GetComponent<Rigidbody2D>().gravityScale = 0f;
newSprite.AddComponent<ObjectMovement>();
newSprite.AddComponent<SpriteRenderer>();
SR = newSprite.GetComponent<SpriteRenderer>();
SR.sprite = sprite;
As you see I added a script "ObjectMovement", I want to check in this script if someone is dragging this particular gameObject and if so, make it follow the touch position, just to mention - this game is 2D. I never used RaysorRaycast so I am not sure where I gone wrong. Anyway here is my script code:
public SpriteRenderer selection=null;
void Update()
{
if (Input.touchCount >= 1)
{
foreach (Touch touch in Input.touches)
{
Ray ray = Camera.main.ScreenPointToRay(touch.position);
RaycastHit hit;
switch (touch.phase)
{
case TouchPhase.Began:
if (Physics.Raycast(ray, out hit, 100))
selection = hit.transform.gameObject.GetComponent<SpriteRenderer>();
break;
case TouchPhase.Moved:
selection.transform.position = new Vector2(selection.transform.position.x + touch.position.x / 10, selection.transform.position.y + touch.position.y / 10);
break;
case TouchPhase.Ended:
selection = null;
break;
}
}
}
}
So basically - when touching the screen, fire a ray and check which gameObject is in this position, when moving the finger make it follow it. Drag and drop. Thanks.
EDIT: I noticed the script is attached to every gameObject which is not effective, any ideas?
For 2D, you use RaycastHit2D and Physics2D.Raycast instead of RaycastHit and Physics.Raycast. Those are for 3D. Secondly, Make sure to attach a collider to the Sprite. Since this is a 2D game, the collider must have word "2D" in it. For example, Box Colider 2D from the Editor. You can also use Circle Collider 2D.
I noticed the script is attached to every gameObject which is not
effective, any ideas?
Just create an empty GameObject and attach that script to it. That's it.
Here is fixed version of your code:
float tempZAxis;
public SpriteRenderer selection;
void Update()
{
Touch[] touch = Input.touches;
for (int i = 0; i < touch.Length; i++)
{
Vector2 ray = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(ray, Vector2.zero);
switch (touch[i].phase)
{
case TouchPhase.Began:
if (hit)
{
selection = hit.transform.gameObject.GetComponent<SpriteRenderer>();
if (selection != null)
{
tempZAxis = selection.transform.position.z;
}
}
break;
case TouchPhase.Moved:
Vector3 tempVec = Camera.main.ScreenToWorldPoint(touch[i].position);
tempVec.z = tempZAxis; //Make sure that the z zxis never change
if (selection != null)
{
selection.transform.position = tempVec;
}
break;
case TouchPhase.Ended:
selection = null;
break;
}
}
}
That would only work on mobile but not on the Desktop Build. I suggest you implement IBeginDragHandler, IDragHandler, IEndDragHandler and override the functions that comes with them. Now, it will work with both mobile and desktop platforms.
Note: For the second solution you have to attach the script below to all Sprites you want to drag unlike the first script above.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class Dragger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Camera mainCamera;
float zAxis = 0;
Vector3 clickOffset = Vector3.zero;
// Use this for initialization
void Start()
{
//Comment this Section if EventSystem system is already in the Scene
addEventSystem();
mainCamera = Camera.main;
mainCamera.gameObject.AddComponent<Physics2DRaycaster>();
zAxis = transform.position.z;
}
public void OnBeginDrag(PointerEventData eventData)
{
clickOffset = transform.position - mainCamera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, zAxis));
}
public void OnDrag(PointerEventData eventData)
{
//Use Offset To Prevent Sprite from Jumping to where the finger is
Vector3 tempVec = mainCamera.ScreenToWorldPoint(eventData.position) + clickOffset;
tempVec.z = zAxis; //Make sure that the z zxis never change
transform.position = tempVec;
}
public void OnEndDrag(PointerEventData eventData)
{
}
//Add Event Syste to the Camera
void addEventSystem()
{
GameObject eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
}
}
There's someone here can help me. Im new to unity. I was working on making objects moves only on x-axis / y-axis when the object touch and drag to the left the object moves to left and stops when the touch or removes on the object.
using UnityEngine;
using System.Collections;
public class MoveScriptVertical : MonoBehaviour
{
//touch offset allows ball not to shake when it start moving
float deltax,deltay;
Rigidbody2D rb;
bool moveAllowed = false;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
}
void Update ()
{
//touch event
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch (0);
//knowing the touch position
Vector2 touchPos = Camera.main.ScreenToWorldPoint(touch.position);
//touch phase
switch (touch.phase)
{
case TouchPhase.Began:
//if you touch the car
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos))
{
//get the offset between position you touch
deltax = touchPos.x - transform.position.x;
deltay = touchPos.y - transform.position.y;
//if touch began within the car collider
//then it is allowed to move
moveAllowed = true;
//restriction some rigidbody properties
rb.freezeRotation = true;
rb.velocity = new Vector2 (0, 0);
GetComponent<BoxCollider2D> ().sharedMaterial = null;
}
break;
//you move your finger
case TouchPhase.Moved:
//if you touches the car and move is allowed
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos) && moveAllowed)
rb.MovePosition (new Vector2 (0, touchPos.y - deltay));
break;
//you released your finger
case TouchPhase.Ended:
//restore intial parameters
//when touch is ended
moveAllowed = false;
rb.freezeRotation = true;
rb.gravityScale = 0;
PhysicsMaterial2D mat = new PhysicsMaterial2D ();
GetComponent<BoxCollider2D> ().sharedMaterial = mat;
break;
}
}
}
}
There are some libraries that allow you to have complex finger events, for instance Touchscript. If you choose this solution you will find tutorials online.
The most basic way is to use unity's Input class with a nice doc here: https://docs.unity3d.com/ScriptReference/Input.html
This allow you to know when a finger touched the screen or moved on the screen. It will give you x an y coordinate so if you just want to move along x you would use only the x value, and, for example translate your object with it using
transform.Translate(new Vector3(x, 0f, 0f));
Edit
Since you use touchscript, have a look at TransformGesture, it should allow you to handle many transform events. If you want to have more control, you can use PressGesture, PanGesture and ReleaseGesture instead
I am trying to check in Unity if a point(x location of touch,y location of touch) is inside a game object created(Rectangle shaped), if yes, start and spin it around.
I am pretty new to Unity but I tried doing it myself, here is what
Rigidbody rb;
float x, y;
MeshCollider col;
Vector3 v;
bool bo = true;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
col = rb.GetComponent<MeshCollider>();
}
void Update()
{
if (bo != true)
rb.transform.Rotate(Vector3.back, Time.deltaTime * 200, Space.World);
if (Input.touchCount == 1)
{
x = Input.GetTouch(0).position.x;
y = Input.GetTouch(0).position.y;
Debug.Log(x + "DOTS " + y);
v = new Vector3(x, y, 0);
if (col.bounds.Contains(v))
bo = false;
}
}
My console is not showing anything, also if I type Debug.Log("HELLO"); and I can't pretty much check myself so that is pretty much my code, appreciate any help.
I think you don't get Log due to you are testing on PC, not on mobile device.
In order to check if the touch point is in bounds of gameObject fire a ray from camera:
#if UNITY_EDITOR
if(Input.GetMouseButtonUp(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
#else
if(Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Ended)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
#endif
int layerMask = (1 << YOUR_TARGET_LAYER_ID);
RaycastHit[] hits = Physics.RaycastAll(ray, 100f, layerMask);
foreach(RaycastHit hit in hits)
{
if(hit.collider == col)
{
Debug.Log("Bingo!!");
break;
}
}
}
Note that I used precompiler directives to make possible this code work on mobile device and on PC.
YOUR_TARGET_LAYER_ID is a layer id of the target you want to only be resposive to ray cast. This will ensure that if some other object is covering the target, it will be ignored.
I'm creating an android app using Andengine. One part of the app requires users to select a few sprites from a group of sprites on the screen, which causes the selected sprites to turn a different color (ie, moving to the next tile). I declared them all as animated sprites and I'm using the same texture for each one. The problem is that once I select a sprite, every sprite moves to the next tile, not just the one I selected. How do I make just the one sprite change?
Here's where I setup the textures and whatnot:
private Texture mGreenTextureAtlas;
private TiledTextureRegion mGreenBallFaceTextureRegion;
#Override
public void onLoadResources() {
/* Textures. */
...
this.mGreenTextureAtlas = new Texture(32, 32, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
...
TextureRegionFactory.setAssetBasePath("gfx/");
/* TextureRegions. */
...
this.mGreenBallFaceTextureRegion = TextureRegionFactory.createTiledFromAsset(this.mGreenTextureAtlas, this, "green_ball.png", 0, 16, 2, 1); // 64x32
this.mEngine.getTextureManager().loadTextures(this.mCueTextureAtlas, this.mGreenTextureAtlas , this.mBackgroundTexture, this.mPocketTexture);
}
Here's where I actually create the sprites and apply the textures:
face = new AnimatedSprite(pX, pY, this.mGreenBallFaceTextureRegion);
body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
encapsed = new Encapsulator(body, face, Encapsulator.AVOID_BALL, mFaceCount);
ballsList.add(encapsed);
I encapsulate each sprite, it's body, and some other data into an object that I made, and then add that object into an ArrayList.
Here is the onTouch event handler.
#Override
public boolean onAreaTouched( final TouchEvent pSceneTouchEvent, final ITouchArea pTouchArea,final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
if(pSceneTouchEvent.isActionDown()) {
final AnimatedSprite face = (AnimatedSprite) pTouchArea;
for(int i=0; i<ballsList.size(); i++)
{
if(face.equals(ballsList.get(i).animatedFace))
{
ballsList.get(i).toggleType(face);
System.out.println("Ball " + ballsList.get(i).id + " is now " + ballsList.get(i).type);
}
}
return true;
}
return false;
}
Finally, here is the toggleType method in the Encapsulator class that's responsible for moving to the next tile:
public void toggleType(AnimatedSprite face)
{
if(this.type == AVOID_BALL)
{
this.type = HIT_BALL;
face.nextTile();
}
else if(this.type == HIT_BALL)
{
this.type = AVOID_BALL;
face.setCurrentTileIndex(0);
}
}
Sorry if this is a bit long-winded. Any help is appreciated.
I did some more googling and came across a solution. I had to use the textureregion.clone() method when creating the sprites. I found the solution at this link:
http://www.andengine.org/forums/development/two-sprites-sharing-the-same-tiledtextureregion-t4339.html