I'm new in Unity and want to create a camera-window like on this website:
http://gamasutra.com/blogs/ItayKeren/20150511/243083/Scroll_Back_The_Theory_and_Practice_of_Cameras_in_SideScrollers.php#h.elfjc4ap4hpe There is a example with Curb Camera Motion. I want to make a camera-window which pushes the camera position as the player hits the window edge.
Any ideas, how to realize this?
I used this code:
using UnityEngine;
using System.Collections;
public class CameraController : MonoBehaviour {
public GameObject player;
public Vector3 min;
public Vector3 max;
private Vector3 offset;
void Start ()
{
offset = transform.position - player.transform.position;
}
void LateUpdate ()
{
Vector3 newPos = player.transform.position + offset;
newPos.x = Mathf.Clamp(x, min.x, max.x);
newPos.y = Mathf.Clamp(x, min.y, max.y);
newPos.z = Mathf.Clamp(x, min.z, max.z);
transform.position = newPos;
}
}
Unfortunately, the camera is moving not correct. Any ideas, how to create a camera-window?
The main issue here is that you need to check your targets position in screen space. Where the object is in screen coordinates. And then move your camera if the target is out of the window in "screen coordinates". The main function to be used here is
Camera.main.WorldToScreenPoint
Following is a basic class that should mimic the effect in the article. This can be improved alot but should be enough to get you started in the right direction.
using UnityEngine;
using System.Collections;
public class CurbCam : MonoBehaviour
{
public Transform targetPosition;
public float camWindowDimension;
Vector2 targetScreenPos;
float deltaX;
float deltaZ;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
//convert target pos to 2D
targetScreenPos = Camera.main.WorldToScreenPoint (targetPosition.position);
if (targetScreenPos.x > (Screen.width/2) + camWindowDimension)
{
deltaX = targetScreenPos.x - ((Screen.width/2) + camWindowDimension);
transform.position = new Vector3(transform.position.x + deltaX, transform.position.y, transform.position.z);
}
if (targetScreenPos.x < (Screen.width/2) - camWindowDimension)
{
deltaX = targetScreenPos.x - ((Screen.width/2) - camWindowDimension);
transform.position = new Vector3(transform.position.x + deltaX, transform.position.y, transform.position.z);
}
if (targetScreenPos.y > (Screen.height/2) + camWindowDimension)
{
deltaZ = targetScreenPos.y - ((Screen.height/2) + camWindowDimension);
transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + deltaZ);
}
if (targetScreenPos.y < (Screen.height/2) - camWindowDimension)
{
deltaZ = targetScreenPos.y - ((Screen.height/2) - camWindowDimension);
transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + deltaZ);
}
}
}
Clamp The Vector3 before applying it to transform.position
using UnityEngine;
using System.Collections;
public class CameraController : MonoBehaviour {
public GameObject player;
public Vector3 min;
public Vector3 max;
private Vector3 offset;
void Start ()
{
offset = transform.position - player.transform.position;
}
void LateUpdate ()
{
Vector3 newPos = player.transform.position + offset;
newPos.x = Mathf.Clamp(x, min.x, max.x);
newPos.y = Mathf.Clamp(x, min.y, max.y);
newPos.z = Mathf.Clamp(x, min.z, max.z);
transform.position = newPos;
}
}
Maybe is not the best approach but you can put 2 colliders at the edges of your Camera and if the player collide with one of them then the Camera will move in the player direction.
In other way you need to calculate the cámera position and the player position both of them referenced at the same point, the center of your scene for example; lets say that your Camera wide is 10 units and for the player are 2 units and both start position is at (0,0).
So, my Camera edges are at (-5,0) & (5,0); and for my player are at (-1,0) & (1,0). Then I need to calculate at every frame the posiiton of my Camera and the player in order to know when the player reach my left or right corner.
Lets say that the Player moves to right until their position is (4,0) it means that his right corner are at (5,0) so, I need to start moving the Camera if the diference betwen distances are higher than certain value (4 in this case because we are walking forward) and the player keeps moving in that direction.
The only thing to take care of is the distance betwen the center of the cámera and the center of the player. If the diference is > to certain number, or lower if you moves backward, then just move the Camera.
Basically is the distance betwen 2 points.
Related
Could anyone tell me how I could make a 3D character move using a mobile joystick? I'm developing for Android on Unity and this one part I'm stuck on, basically I want to be able to move the character with a joystick and then be able to rotate through touching the screen.
For the plugged joystick, it's basically like any PC joystick. So you just have to use the old fashioned Input.GetAxis("Vertical") and Input.GetAxis("Horizontal") method. Be sure to map those in input from all joysticks in your Project preference > Inputs.
Official doc
As for the touch, it also works easily with Input.GetTouch(0).position
Official doc
You may also want to raycast the touch tap in order to get the world-scale position of the position you are touching:
private static Vector3 GetPositionFromScreenPosition(Vector3 screenPosition)
{
Ray ray = Camera.main.ScreenPointToRay(screenPosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
return hit.point;
}
return Vector3.zero;
}
Both works well on the same Update() or FixedUpdate() method.
This is my current code:
using UnityEngine;
using UnityEngine.AI;
public class PlayerMover : MonoBehaviour {
public Joystick joystick;
public float characterSpeed = 175f;
public float turnSpeed = 10f;
void Start() {
joystick = FindObjectOfType<Joystick>();
}
void Update() {
var rigidbody = GetComponent<Rigidbody>();
rigidbody.velocity = new Vector3(joystick.Horizontal * characterSpeed, rigidbody.velocity.y, joystick.Vertical * characterSpeed);
float turn = Input.GetAxis("Horizontal") * turnSpeed;
transform.Rotate(0, turn * turnSpeed * Time.deltaTime, 0);
UpdateAnimator();
}
private void UpdateAnimator() {
Vector3 velocity = GetComponent<Rigidbody>().velocity;
Vector3 localVelocity = transform.InverseTransformDirection(velocity);
float speed = localVelocity.z;
GetComponent<Animator>().SetFloat("movementSpeed", speed);
}
}
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 make it so that the CardboardMain in Unity will slowly drift in the direction that the center point of the VR is pointing. I have the script:
using UnityEngine;
using System.Collections;
public class Move : MonoBehaviour {
public float balloon_speed = 0.0001f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
//float rotLeftRight = Input.GetAxis ("Mouse X");
//transform.Rotate (0, rotLeftRight, 0);
Vector3 direction = new Vector3(0,0,balloon_speed);
direction = transform.rotation * direction;
transform.localPosition += direction;
}
}
If the lines
//float rotLeftRight = Input.GetAxis ("Mouse X");
//transform.Rotate (0, rotLeftRight, 0);
are un-commented, the script works perfectly in Unity. When I load it to an android device, the camera will drift forwards and the direction won't change. I think the reason for this is that the VR coordinates are different from what transform.rotaion will return. Any advice?
I'd try this:
void Update() {
transform.localPosition += balloon_speed * Vector3.forward;
}
I think in your script you were adding a world-coordinate vector (rotation * direction) to the local-coordinate position.
I'm making a game with Andengine and i'm stuck for 2 days on shooting from a rotating sprite. I'm not a hero at geometry and already ask a teacher but he could also not provide me the correct answer. So who is a mathematics hero and help me out :).
The problem is that i cannot figure out where the bullet has to spawn in front of the turret. Rotating and finding the destination where the bullet has to go is no problem. It only about the spawn point.
I removed a lot of not-interesting-code for this question.
Okay so here is the rotating code from the turret-rotation:
public class AlienShip extends Ship {
public static final float BASE_ROTATION_SPEED = 0.25f;
public static final int DEFAULT_IMAGE_ROTATION = 90; //90 degrees
protected PlayerShip ship;
public AlienShip(float pX, float pY, TextureRegion pTextureRegion,
VertexBufferObjectManager pVertexBufferObject,FixedStepPhysicsWorld pw, int baseDurability) {
super(pX, pY, pTextureRegion, pVertexBufferObject, pw, baseDurability);
}
public void rotateToPlayer()
{
if (ship != null) {
float dX = this.getX() - ship.getX();
float dY = this.getY() - ship.getY();
float angle = (float) Math.atan2(-dY, dX);
float rotation = MathUtils.radToDeg(angle) + DEFAULT_IMAGE_ROTATION;
RotationModifier rotMod = new RotationModifier(BASE_ROTATION_SPEED, this.getRotation(), rotation);
this.registerEntityModifier(rotMod);
}
}
public void rotateToInitPos() {
RotationModifier rotMod = new RotationModifier(BASE_ROTATION_SPEED, this.getRotation(), 0);
this.registerEntityModifier(rotMod);
}
}
The code above is working fine.
Here is the code from the laser that the ship is shooting.
Read the comments to find out witch part is not working.
public class GameScene extends Scene {
protected PlayerShip playerShip;
private SpawnCallback createShootCallback(boolean player) {
return new SpawnCallback() {
#Override
public void spawn(SpawnTimer spawnTimer) {
PhysicsSprite laser = null;
AlienShip alienShip = (AlienShip) spawnTimer.getPhysicsSprite();
// laser = alienMissilePool.getMissileFromPool(x,y)
//spawn the laser in front of the rotating ship [Not working :( ]
laser = alienMissilePool.getMissileFromPool( ( alienShip.getX() * FloatMath.cos(MathUtils.degToRad(rotation)) - ((1280 - alienShip.getY() - alienShip.getY()/2) * FloatMath.sin(MathUtils.degToRad(rotation)) ) ) ,
( alienShip.getX() * FloatMath.sin(MathUtils.degToRad(rotation)) + ((1280 - alienShip.getY() - alienShip.getY()/2) * FloatMath.cos(MathUtils.degToRad(rotation)) ) ) );
//Set the rotation from the laser same to the ship rotation [Is working perfectly].
float rotation = alienShip.getRotation();
laser.setRotation(rotation);
//Set laser speed and direction [Is working perfectly]
float pX = 0.01f * (playerShip.getX() - laser.getX());
float pY = 0.01f * (playerShip.getY() - laser.getY());
laser.getSpriteBody().setLinearVelocity(pX, pY);
spawnPhysicsSprite(laser);
}
};
}
}
Here is a link to a drawing that shows the x-axis and y-axis values.
http://s24.postimg.org/citz29339/gamescene.png
Thank you!
Instead of getting into maths, why don't you put an object (Entity) positioned on the place of the turret and use it's position as the spawn point for the laser ?
so your turret would have an Entity attached to it on the place of the gun.
tell me if you need an example code
I am in the process of learning cocos2d-android. I have been following a tutorial that ported some of Ray Wenderlich's iOS tutorials to Android. I have the first tutorials finished and wanted to continue on by converting the next Ray Wenderlich tutorial to Android by myself as an exercise in learning.
The original iOS tutorial can be found here http:// www raywenderlich com/1163/how-to-make-a-tile-based-game-with-cocos2d
I have converted the application to android but am having some trouble with the way its behaving.
My code is here:
public class GameLayer extends CCLayer{
private CGSize _winSize;
protected ArrayList<CCSprite> _targets;
protected ArrayList<CCSprite> _projectiles;
protected int _projectilesDestroyed;
protected CCSprite _player;
protected CCSprite _nextProjectile;
protected CCTMXTiledMap _map;
protected CCTMXLayer _background;
protected CCTMXObjectGroup _objects;
protected HashMap<String, String> _spawnPoint;
protected GameLayer() {
super();
_winSize = CCDirector.sharedDirector().displaySize();
_targets = new ArrayList<CCSprite>();
_projectiles = new ArrayList<CCSprite>();
_projectilesDestroyed = 0;
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_background);
_spawnPoint = _objects.objectNamed("SpawnPoint");
_player = CCSprite.sprite("Player3.png");
setPlayerPosition(CGPoint.ccp (100.0f, 100.0f));
addChild(_player);
setViewPointCentered(_player.getPosition());
Context context = CCDirector.sharedDirector().getActivity();
SoundEngine.sharedEngine().preloadEffect(context, R.raw.pew_pew_lei);
SoundEngine.sharedEngine().playSound(context, R.raw.background_music_aac, true);
this.setIsTouchEnabled(true);
this.schedule("update");
}
public void setViewPointCentered(CGPoint pos) {
float x = 0.0f;
float y = 0.0f;
x = Math.max(pos.x, _winSize.width / 2);
y = Math.max(pos.y, _winSize.height / 2);
x = Math.min(x, (_map.getMapSize().width * _map.getTileSize().width) - _winSize.width / 2 );
y = Math.min(y, (_map.getMapSize().height * _map.getTileSize().height) - _winSize.height / 2);
CGPoint actualPos = CGPoint.ccp(x, y);
CGPoint centerOfView = CGPoint.ccp(_winSize.width / 2, _winSize.height / 2);
CGPoint viewPoint = CGPoint.ccpSub(centerOfView, actualPos);
_background.setPosition(viewPoint);
}
public static CCScene scene() {
CCScene scene = CCScene.node();
CCLayer layer = new GameLayer();
scene.addChild(layer);
return scene;
}
#Override
public boolean ccTouchesBegan(MotionEvent event) {
return true;
}
void setPlayerPosition(CGPoint position) {
_player.setPosition(position);
}
#Override
public boolean ccTouchesEnded(MotionEvent event) {
// Choose one of the touches to work with
CGPoint touchLocation = CGPoint.ccp(event.getX(), event.getY());
touchLocation = CCDirector.sharedDirector().convertToGL(touchLocation);
touchLocation = this.convertToNodeSpace(touchLocation);
CGPoint playerPosition = _player.getPosition();
CGPoint diff = CGPoint.ccpSub(touchLocation, playerPosition);
if (Math.abs(diff.x) > Math.abs(diff.y)) {
if (diff.x > 0) {
playerPosition.x += _map.getTileSize().width;
} else {
playerPosition.x -= _map.getTileSize().width;
}
} else {
if (diff.y > 0) {
playerPosition.y += _map.getTileSize().height;
} else {
playerPosition.y -= _map.getTileSize().height;
}
}
if (playerPosition.x <= (_map.getMapSize().width * _map.getTileSize().width) &&
playerPosition.y <= (_map.getMapSize().height * _map.getTileSize().height) &&
playerPosition.y >= 0 &&
playerPosition.x >= 0 ) {
setPlayerPosition(playerPosition);
}
setViewPointCentered(_player.getPosition());
return true;
}
public void finishShoot() {
addChild(_nextProjectile);
_projectiles.add(_nextProjectile);
}
public void update(float dt) {
ArrayList<CCSprite> projectilesToDelete = new ArrayList<CCSprite>();
for (CCSprite projectile : _projectiles) {
CGRect projectileRect = CGRect.make(projectile.getPosition().x - (projectile.getContentSize().width / 2.0f),
projectile.getPosition().y - (projectile.getContentSize().height / 2.0f),
projectile.getContentSize().width,
projectile.getContentSize().height);
ArrayList<CCSprite> targetsToDelete = new ArrayList<CCSprite>();
for (CCSprite target : _targets) {
CGRect targetRect = CGRect.make(target.getPosition().x - (target.getContentSize().width),
target.getPosition().y - (target.getContentSize().height),
target.getContentSize().width,
target.getContentSize().height);
if (CGRect.intersects(projectileRect, targetRect)) {
targetsToDelete.add(target);
}
}
for (CCSprite target : targetsToDelete) {
_targets.remove(target);
removeChild(target, true);
}
if (targetsToDelete.size() > 0) {
projectilesToDelete.add(projectile);
}
}
for (CCSprite projectile : projectilesToDelete) {
_projectiles.remove(projectile);
removeChild(projectile, true);
if (++_projectilesDestroyed > 30) {
_projectilesDestroyed = 0;
CCDirector.sharedDirector().replaceScene(GameOverLayer.scene("You Win!"));
}
}
}
}
I first grab my display size and create my tiled map from my TMX file. I get my background layer and add it as a child. I then grab my objects layer and pull my spawn point object out of the map (I override this spawn point with 100, 100 for testing purposes). I grab my player sprite and set my player position to the 100, 100 coordinates. I then add the player as a child.
Next I call setViewPointCentered to move my map to an appropriate position based on my players position. This part works just fine and my map gets placed with the lower left corner (0,0) placed in the lower left corner (0,0) of my screen and my character is at 100,100 slightly left and down from the center of the screen.
The problem occurs when I begin moving up or right. Once I pass the center of the screen I would expect to have the player sprite stay centered on the screen and the background move the opposite direction as I continue moving. However both the player and the background move so eventually my player comes to the right or top edge of the screen and I can't move up or right any longer even though there is plenty of map left.
Notice the player in the upper left corner of the map.
Player reaching the top of screen and not staying centered as expected
Notice the player in the lower right corner of the map.
Player reaching the right of screen and not staying centered as expected
The "public boolean ccTouchesEnded(MotionEvent event)" method and the "public void setViewPointCentered(CGPoint pos)" method handle the player and view positioning but I don't think they're working correctly.
A friend of mine does iOS programming and created the app on his iPhone and it's working as expected so I'm wondering if there is a bug in the android port of cocos2d.
Does anyone have any ideas on why the character won't stay centered on the screen when I get to the middle and continue moving right or up on the map?
Thanks for any input you can provide. I've been beating my head against my desk for two days trying to figure this out.
OK I figured it out.
In this piece of code:
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_background);
I am adding my _background layer but what I really want to be doing is adding my _map instead:
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_map);
Now my player character remains in the center of my viewable screen while walking around unless I get to the edge of the map.
Each time when the touch ends, the player is moved by mapWidth or height. You should move the position with diff. Try
CGPoint diff = CGPoint.ccpSub(touchLocation, playerPosition);
if (Math.abs(diff.x) > Math.abs(diff.y)) {
if (diff.x > 0) {
playerPosition.x += diff.x;
} else {
playerPosition.x -= diff.x;
}
} else {
if (diff.y > 0) {
playerPosition.y += diff.y;
} else {
playerPosition.y -= diff.y;
}
}