How do you add multi-tap (not multi-touch!) input to a Unity3D game? I have a hard time finding any useful information about this.
What I got so far is multi-touch input support. this is what I'm using for that:
private Vector2 _touchOrigin = -Vector2.one;
public bool TouchEnded(int touchCount = 1)
{
if (Input.touchCount != touchCount) return false;
Touch lastTouch = Input.GetTouch(touchCount - 1);
if (lastTouch.phase == TouchPhase.Began)
{
_touchOrigin = lastTouch.position;
}
else if (lastTouch.phase == TouchPhase.Ended && _touchOrigin.x >= 0)
{
_touchOrigin.x = -1;
return true;
}
return false;
}
What I would like to do is write a similar method but for multi-tap. I.e. the user has to tap several fingers (touchCount) simultaneously a number of times (tapCount). This would be the method signature:
public bool TapEnded(int touchCount = 1, int tapCount = 2)
{
}
Can somebody give me some help how to get this requirement working?
You can use Input.Touches Returns list of objects representing status of all touches during last frame. reference to link http://docs.unity3d.com/ScriptReference/Input-touches.html
void Update() {
int fingerCount = 0;
foreach (Touch touch in Input.touches) {
if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
fingerCount++;
}
if (fingerCount > 0)
print("User has " + fingerCount + " finger(s) touching the screen");
}
Related
I have a problem in which my ammo won't decrease when I shoot until I wait for a certain Amount of time. I'm currently using Photon as my Network System for my game and my gun code from Brackeys (https://www.youtube.com/watch?v=kAx5g9V5bcM&lc=UghlAulu5dH90HgCoAEC). This is maybe related to internet problems and delays but I'll show you the code I use:
void Update()
{
if(!photonView.IsMine) return;
if (!canShowAmmo)
{
ammoText.text = "Ammo: " + currentAmmo;
}
if (isStarted && (owner.openPanel || MultiplayerManager.main.roomManager.gameDone))
{
if (isScoped)
{
Scope();
}
return;
}
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
if(autoOrSemi == true){
if(Input.GetButton("Fire1") && Time.time >= nextTime){
nextTime = Time.time + 1f/fireRate;
Shoot();
}
}
if(autoOrSemi == false){
if(Input.GetButtonDown("Fire1") && Time.time >= nextTime){
nextTime = Time.time + 1f/fireRate;
Shoot();
}
}
if (Input.GetButtonDown("Fire2"))
{
Scope();
}
}
public void Shoot(){
MuzzleFlash.Play();
currentAmmo--;
AudioSource.PlayClipAtPoint(shotSound, transform.position);
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range)){
Debug.Log(hit.transform.name);
IDamageable target = hit.collider.gameObject.GetComponent<IDamageable>();
if (target != null)
{
if (hit.collider != owner.GetComponent<Collider>())
{
target.TakeDamage(damage);
owner.score += (int) damage;
owner.ScoreUpdate();
}
}
if(hit.rigidbody != null){
hit.rigidbody.AddForce(-hit.normal * impactForce);
}
GameObject effect = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(effect, 2f);
}
}
public IEnumerator Reload(){
isReloading = true;
Debug.Log("Reloading");
canShowAmmo = true;
ammoText.text = "Reloading...";
yield return new WaitForSeconds(reloadTime - .25f);
yield return new WaitForSeconds(.25f);
canShowAmmo = false;
currentAmmo = maxAmmo;
isReloading = false;
}
so if I was correct and it's caused by lag or I was wrong and didn't have any relation to the Network, please I need a solution. Thanks.
Sorry my bad, I may just found a solution: just add a new private bool and set it to true inside the if function derHugo presented below or above this comment and if bool is false then shooting is disabled then after reloading inside the enumerator after isReloading = false, make the bool to false
I think you issue are concurrent Coroutines!
you are doing
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
This will start a new routine every frame while the condition is met.
You rather want to do e.g.
if (!isReloading && currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
if(isReloading) return;
I am trying to make enemy patrolling system, where evrytime guard reaches his point, he stopes for 10 seconds, and then continue his movement. I've tried combining animations from Blend tree with isStopped property from NavMeshAgent.
EDIT: My current script makes agent move to point, then he stopes for some time, and then only walk animation plays, but he staing on one place.
public Transform[] points;
private int destPoint = 0;
public NavMeshAgent agent;
public Animator animator;
public int time;
void Start()
{
agent = GetComponent<NavMeshAgent>();
animator = transform.Find("Enemy").GetComponent<Animator>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
//agent.autoBraking = false;
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Length == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Length;
//agent.speed = 1f;
//animator.SetFloat("Blend", agent.speed);
}
void Update()
{
if (agent.remainingDistance == 0f && time == 100000)
{
agent.speed = 1f;
Debug.Log(agent.remainingDistance);
animator.SetFloat("Blend", 1);
GotoNextPoint();
}
else if (agent.remainingDistance <= 0.5f && agent.remainingDistance != 0f && time == 100000)
{
animator.SetFloat("Blend",0);
agent.enabled = false;
GotoNextPoint();
}
else if(animator.GetFloat("Blend") == 0)
{
time--;
}
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
}
I changed few lines of code, now agent moves after first stop, but second time he stops at second poitm,walking animation still working, time doesn't decrementing
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
//New lines of code
agent.ResetPath();
destPoint = (destPoint + 1) % points.Length;
agent.SetDestination(points[destPoint].position);
}[enter image description here][1]
First of all, I would use the "SetDestination" function in order to set the next destination.
In the end you wrote:
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000; *-> needs to be 100000, not 10000*
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
You can use "NavMeshAgent.ResetPath" to reset the path instead of using "autoRepath".
After the "ResetPath", use the "SetDestination" inside the function GotoNextPoint().
One more very important thing - Check that there is more than one point.
If there is just one point, your guard will just walk at the same spot.
For more information, is suggest to check out Unity NavMeshAgent
I'm working on a snake game and I'm running into a problem where if I hit my inputs (up, down, left, right) too fast, my snake runs into itself. I've tried to create a timer under Update() to regulate my movements but I find my snake to still be running into itself from time to time.
I use InvokeRepeating() to move my snake every 0.05 seconds and I have 2 methods of inputs: 1) Update to listen for keyboard inputs (up, down, left, right arrows) and 2) a Gamepad UI (up, down, left, right buttons). Any suggestions to how I could regulate my directional changes?
void Start () {
Screen.orientation = ScreenOrientation.Portrait;
isPaused = false;
InvokeRepeating("TimerInvoke", 0, deltaTimer);
}
void TimerInvoke() {
Movement();
StartCoroutine(checkVisible());
if (currentSize == maxSize)
TailFunction();
else
currentSize++;
}
void Update () {
CompDirectionControl();
PauseCMD();
}
void CompDirectionControl() {
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
direction = Direction.North;
if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
direction = Direction.East;
if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
direction = Direction.South;
if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
direction = Direction.West;
}
public void MPadDirectionControl(int numDirection)
{
if (direction != Direction.South && numDirection == 0)
direction = Direction.North;
if (direction != Direction.West && numDirection == 1)
direction = Direction.East;
if (direction != Direction.North && numDirection == 2)
direction = Direction.South;
if (direction != Direction.East && numDirection == 3)
direction = Direction.West;
}
I tried creating something like this but the delay is very inconsistent
if (Time.time >= timeStamp)
{
CompDirectionControl();
timeStamp = Time.time + moveInterval;
}
UPDATE
I was able to add a delay to my keyboard inputs by using a coroutine. However, I have not figured out how to do the same with my button UI inputs. If I create a public IEnumerator GamePadDirControl(int direction), how do I pass my button's parameter to my IEnumator? Even though I set my GamePadDirControl to public, I don't see it in my Inspector so I can't link my button to it
IEnumerator DirControl() {
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.North;
}
else if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.East;
}
else if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.South;
}
else if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.West;
}
}
UPDATE 2
For my buttons UI - Since it isn't possible for my button to link up with IEnumator functions, I decided to create individual coroutines for each direction and call on them from my public void MPadDirectionControl(int numDirection) function.
updated code on button ui input
public void MPadDirectionControl(int numDirection)
{
if (direction != Direction.South && numDirection == 0)
{
StartCoroutine(IntervalN());
//direction = Direction.North;
}
else if (direction != Direction.West && numDirection == 1)
{
StartCoroutine(IntervalE());
//direction = Direction.East;
}
else if (direction != Direction.North && numDirection == 2)
{
StartCoroutine(IntervalS());
//direction = Direction.South;
}
else if (direction != Direction.East && numDirection == 3)
{
StartCoroutine(IntervalW());
//direction = Direction.West;
}
}
IEnumerator IntervalN() {
yield return new WaitForSeconds(deltaTimer);
direction = Direction.North;
}
IEnumerator IntervalE()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.East;
}
IEnumerator IntervalS()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.South;
}
IEnumerator IntervalW()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.West;
}
Not sure i fully understand your question ...
Your trying to change the direction before a delay ?
If so, I will try:
StartCoroutine(ChangeDirectionTo(Direction.North, 0.05f));
IEnumarator ChangeDirectionTo(Direction direction, float delay) {
yield return new WaitForSeconds(delay);
this.direction = direction;
}
I think I see the problem. You need to distinguish between the current direction of the snake, and the user's commanded direction. Try something like this:
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
nextDirection = Direction.North;
if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
nextDirection = Direction.East;
if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
nextDirection = Direction.South;
if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
nextDirection = Direction.West;
}
Then in the code where you actually create the cube, just before spawning it, use nextDirection to tell you where the player wants to go:
direction = nextDirection;
Thus the player simply doesn't have the chance to change the direction before the cube is spawned.
This should work, but it has the disadvantage that you won't get the nice "input buffering" effect that some snake games have, where you can tap Right, Down while the snake is going north to do a U-turn. If you want to do that, you'll have to implement some kind of smart buffering system that detects if the player puts in a very specific kind of "invalid" input (north/south when direction is north and nextDirection is east) and sets the snake up for a future direction change. Kind of like buffering "special move" inputs for a fighting game.
I've formulated my own collision system in a modified version of OpenTK. It works by running a foreach loop which checks all the quads in the game (this runs when something moves) and sets their position back to where it was last frame, that is if it intersects this frame. I might not have explained that well, so here is the SetXVelocity code, called when a player moves right.
public void SetXVelocity(float x)
{
foreach (Quad quad in quads)
{
if (!quad.Equals(this))
{
if (Intersects(quad))
{
Velocity.x = 0;
Position = OldPosition;
continue;
}
if (!Intersects(quad))
{
OldPosition = Position;
Velocity.x = x;
continue;
}
}
else
{
OldPosition = Position;
continue;
}
OldPosition = Position;
continue;
}
}
Here is the code for Intersects:
public bool Intersects(Quad quad)
{
#region
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if (BRCorner.x < quad.BRCorner.x && BRCorner.x > quad.TLCorner.x && BRCorner.y > quad.BRCorner.y && BRCorner.y < quad.TLCorner.y)
{
return true;
}
if (TRCorner.x < quad.TRCorner.x && TRCorner.x > quad.BLCorner.x && TRCorner.y < quad.TRCorner.y && TRCorner.y > quad.BLCorner.y)
{
return true;
}
if (BLCorner.x > quad.BLCorner.x && BLCorner.x < quad.TRCorner.x && BLCorner.y > quad.BLCorner.y && BLCorner.y < quad.TRCorner.y)
{
return true;
}
#endregion // Corner Intersection
if (Math.Round(Left, 2) == Math.Round(quad.Right, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
if (Math.Round(Right, 2) == Math.Round(quad.Left, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
return false;
}
By the way, TRCorner is a Vector3 representing the Top Right Corner etc.
Last block of code is the Quad class, keep in mind, the actual class is huge, so I'll try not include all of it:
public Vector3 Velocity;
public Vector3 Position;
public int Index;
public VBO<Vector3> VertexData
{
get;
set;
}
public VBO<int> VertexCount
{
get;
set;
}
public VBO<Vector2> VertexTexture
{
get;
set;
}
For some reason, a few quads have no collision and some do. Some even have collision that makes the player stick to them.
I think some quads are missing because all possible conditions are not covered in your comparisons.
Since you && all your conditions, a collision needs to meet all your conditions to get detected.
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if( TLCorner.x == quad.TLCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y ) the rectangle still collides, but undetected.
Same for other conditions.
You might want to use >= and <= in your code in order to cover all conditions.
As for why they get stuck in one another, your velocity probably pushes them into a condition which makes them collide forever, since you detect the collision after they went into each other and not when the boundaries overlap.
Also you might want to reverse the velocity and "un-stuck" them when collision is detected in a way that does not make them stuck in other nearby rectangles.
I highly recommend you use better logic for collision detection
Link: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
You can test it here http://jsfiddle.net/knam8/
This is a code I made but when I test it on my phone it freezes and nothing happens (same in the editor mode when I'm connected with my phone via Remote4) So What I would like to is to change cameras by just a touch on the screen any time. How should I make it?
And what's the matter when I would like to change the Camera from mainCam to topCam only when the screen is touched, and when I release the screen with my finger it changes back to mainCam from topCam?
This code goes into Update()
while (Input.touchCount > 0) {
for (int i = 0; i < Input.touchCount; i++) {
if (Input.GetTouch (i).phase == TouchPhase.Ended && Input.GetTouch(i).tapCount == 1) {
if (mainCam.enabled){
mainCam.enabled = false;
topCam.enabled = true;
} else {
mainCam.enabled = true;
topCam.enabled = false;
}
}
}
}
Problem
Your loops are the problem and some weird logic with the ifs.
Touch and Hold Solution
void Update()
{
if(Input.touchCount > 0)
{
if(Input.GetTouch(0).phase == TouchPhase.Began)
{
topCam.enabled = true;
mainCam.enabled = false;
}
if(Input.GetTouch(0).phase == TouchPhase.Ended
|| Input.GetTouch(0).phase == TouchPhase.Canceled)
{
mainCam.enabled = true;
topCam.enabled = false;
}
}
}
Tab Switch Solution
void Update()
{
if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
topCam.enabled = !topCam.enabled;
mainCam.enabled = !mainCam.enabled;
}
}