Sound for collision only once - triggers

Ive got the following code to check for agent collision.
I want to fire a MIDI message only once when they start colliding.
Ive got this so far.
void draw(){
//Loop through people, and check collision, then play note, if intersecting
for(int i=0;i<people.size();i++){
Person p = people.get(i);
p.collide(people,collisions);
p.triggerMidi();
p.run();
}
}
public void collide(ArrayList<Person> people, ArrayList<Person> connections) {
for(Person other : people) {
if (other != this) {
if (this.collide(other)) {
this.isIntersecting=true;
//connections.add(other); // when a collision is found, add it to a list for later use.
}
}
}
}
void triggerMidi(){
if(!hasPlayed && this.isIntersecting==true){
MIDI.sendNoteOn(channel, agentNote, 127);
delay(200);
MIDI.sendNoteOff(channel,agentNote, 127);
hasPlayed=true;
}
}
This works to play the sound only once at the start of collision.
But how do I get it to play again at the start of another collision.
Obviously I have to set hasPlayed back to false.
But where?
When I set it to false in the collide loop, the sound play a million times.
Any ideas?

First off, you probably shouldn't have a call to delay() from your drawing thread. That will cause your sketch to become laggy and unresponsive. Instead, you might want to put your sound playing on a different thread.
Then, to answer your original question- do you know how long the note plays for? If so, just record the time that the note starts, and then use that time to check the elapsed time. The millis() function might come in handy for that. When the elapsed time is greater than the duration of the note, then you can set hasPlayed back to false.

Related

Game Center Turn Timeout for Multiplayer GAmes

I have created a turn based multiplayer board game using Swift and Game Center that works pretty well. One of the last items I would like to add is a way to keep a player from abandoning a game near the end if they know they are going to lose. It seems like the turnTimeout portion of the endTurn function is built in especially for this purpose, but I cannot get it to work. My endTurn function is below:
func endTurn(_ model: GameModel, completion: #escaping CompletionBlock) {
guard let match = currentMatch else {
completion(GameCenterHelperError.matchNotFound)
return
}
do {
let currenParticipantIndex: Int = (match.participants.firstIndex(of: match.currentParticipant!))!
let nextPerson = [match.participants[(currenParticipantIndex+1) % match.participants.count]]
print("end turn, next participant \(String(describing: nextPerson[0].player?.alias))")
match.endTurn(
withNextParticipants: nextPerson,
turnTimeout: 15,
match: try JSONEncoder().encode(model),
completionHandler: completion
)
} catch {completion(error)}
}
This function takes into account the advice from Anton in the comment of the answer to this question:
Trying to set a time limit on my Game Center game
to update the array of nextParticipant players so that the end of the array is never reached. I've also tried to account for this in my testing by having both player 1 and player 2 delay the end of their turn to see if it would fire (The game is a 2 player game only)
This should also answer this question:
Game Center turn timeouts
The documentation says:
timeoutDate: The time at which the player must act before forfeiting a turn. Your game decides what happens when a turn is forfeited. For some games, a forfeited turn might end the match. For other games, you might choose a reasonable set of default actions for the player, or simply do nothing.
https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/GameKit_Guide/ImplementingaTurn-BasedMatch/ImplementingaTurn-BasedMatch.html
Unfortunately I am unable to get the turnTimeout function to fire at all. I have done a fair amount of research and I found no definitive answer to what function is actually called when it fires (i.e. the player takes longer than the allotted time to take their turn). I would expect that the same function is called for a timeout as a regular call of the endTurn function and the below player listener is called:
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
if let vc = currentMatchmakerVC {
currentMatchmakerVC = nil
vc.dismiss(animated: true)
}
print("received turn event")
if !didBecomeActive {
print("\n\n\n player listener did become active")
print("match did change")
NotificationCenter.default.post(name: .matchDidChange, object: match)
} else if didBecomeActive {
print("present game")
NotificationCenter.default.post(name: .presentGame, object: match)
}
}
I am able to get the player listener (received turn event) to fire when endTurn is specifically called from the game, but I do not see anything that is called when the turnTimeout event triggers. If it was the player listener I would see the print statements in the console as well as the notification on the next player's device.
The GKTurnTimeoutDefault is 604,800 and is a Time Interval which I did some research on and arrived at the conclusion that it is in seconds, which is 7 days. I changed it to 0.00001, 15, 2000 and a few values in between but I wasn't able to get it to fire.
I also found the below, but the first has no answer and the second only says the turn timeouts probably warrants its own full answer:
Game Center Turnbased Game turn timeout
How to detect when Game Center turn based match has ended in iOS9?
I am thinking that my mistake is probably that I am unable to find the function that is called when the turn timeout fires, although I might be mistaken on the Time Interval values that I'm putting in there as well.
Thank you for taking the time to review my question :)

UNITY | Update or InvokeRepeating?

I use many timers in my Unity project, they done like this:
void Update()
{
timer -= Time.deltaTime;
if(timer < 0)
{
DoSomething();
}
}
And in every google link they looks like this.
But today i found (im newbie) InvokeRepeating(); method.
So here is updated timer
int timer = 60;
void Start()
{
InvokeRepeating("Timer", 1f, 1f);
}
void Timer()
{
timer -= 1;
}
So why people change timers in Update()?
And which method less inpact on performance?
Depends!
As usual in programming there are most of the time multiple valid solutions for a problem.
In my eyes the bigest difference is that InvokeRepeating is also working on inactive GameObjects or disabled Components while Update is only called while the object is active and the component enabled.
Note, however, that your current examples do different things. To make them equivalent it should look like either
void Start()
{
timer = 1f;
}
void Update()
{
timer -= Time.deltaTime;
if(timer < 0)
{
timer = 1f;
DoSomething();
}
}
or
void Start()
{
InvokeRepeating(nameof(DoSomething), 1f, 1f);
}
Btw: A third basically equivalent solution would be a Coroutine (which is basically a temporary Update method - in fact the MoveNext call gets executed right after the Update would)
// Yes, Start can be an IEnumertaor and is in this case internally implicitly started as Coroutine!
IEnumerator Start()
{
while(true)
{
yield return new WaitForSeconds(1f);
DoSeomthing();
}
}
As mentioned by Kuruchy: There is also a difference in behavior related to the timeScale.
Update: since it uses Time.deltaTime to decrease the timer it will be affected by the time scale
→ in order to avoid this you would need to use Time.unscaledDeltaTime instead. But afaik still if setting Time.timeScale = 0; then Update isn't called at all.
Coroutine: Similar to update the WaitForSeconds is also timeScale dependent.
→ in roder to avoid this you would need to use WaitForSecondsRealitme but afaik even then setting Time.timeScale = 0; would lead to the routine not getting called at all.
InvokeRepeating: Fromt he docs I can only see that
This does not work if you set the time scale to 0.
not sure how it reacts to the timescale though but I would actually guess the same. Though, here there is no real work around like for the other two.
Performance wise you most probably don't even have to care! I guess it would be over-optimizing.
However my guess would be that Update is actually slightly faster since there you already know the method reference while when using InvokeRepeating you are passing it as a string which is additionally error prone and means that internally Unity has to find that method first.
Without going into too much detail is because of the performance.
The Update() method is invoked internally by Unity and they've done a pretty good job in optimizing it.
InvokeRepeating is much slower in comparison. First of all because the initial method invocation is using the Reflection to find the method you want to start and it's respective calls also take more time than Update. And you want to avoid using Reflection in your code as much as possible.
Here is nice, short article in which the tests were performed between these two methods - http://www.kittehface.com/2017/09/unity-performance-with-invokerepeating.html

Unity timeline, playing animation clips and controlling playback on events

I'm wondering if I can get some help on understanding how to use the timeline
I plan to have the character walking along a path with triggers that will activate the timeline,
I have set up a simple state machine on the monkey
which would be triggered by the path to play the timeline, that part works fine and I can see the events firing
How could I play part of the timeline when triggered (is that even possible?)
where the entire sequence is held Walk>Jump>Walk>Look>Walk>Idle
Or what would I need to do to be able to achieve playback of a clip/or part of a sequence of clips
On the monkey:
public enum State
{
WalkHappy,
Look,
Jump,
}
IEnumerator WalkHappyState()
{
Debug.Log("WalkHappy: Enter");
**//walk sequence with timeline here??**
while (state == State.WalkHappy)
{
yield return 0;
}
Debug.Log("WalkHappy: Exit");
NextState();
}
Called on the paths event listener when triggered
private void OnCustomEvent(string eventname)
{
if (eventname.Contains("MonkeyRunningJumpUp"))
{
GameObject tempMonkey = GameObject.FindGameObjectWithTag("Player");
Monkey tempMonkeyState = tempMonkey.GetComponent<Monkey>();
tempMonkeyState.state = Monkey.State.Jump;
}
}
I have seen its possible to control where the playback starts on a timeline, but I don't know how to force a range/clip to play then pause at the end of the clip rather than the end of the entire sequence
THE question is.. how to stop/pause at end of a clip to resume later?
playableDirector = myPlayableDirectorGO.GetComponent<PlayableDirector>();
Debug.Log(timeLinePosition);
playableDirector.time = timeLinePosition / ((TimelineAsset)playableDirector.playableAsset).editorSettings.fps;
playableDirector.RebuildGraph();
playableDirector.Play();
with thanks Natalie :)
First of all, I recommend to install Default Playables if you haven’t done it yet.
Use Timeline Signals and markers to do something at the end of timeline or at certain point. Also, there is PlayableDirector.stopped event.
You can use pause and resume methods;

Randomized Attack Animation RPG

First of all, I'm quite noob with animator and animation systems in unity.
What I'm trying to achieve (and I was trying with Animator component) is a randomized attack only while I keep my mouse button pressed on the enemy and which completes the execution of the attack clip that is playing even if i release the button meanwhile.
I tried adding my 2 attack animations to a list and play it, with something like
anim.Play(Random.Range(0, list.Count))
...but I don'tknow if the problem is that while I keep pressed one animation cancels the other or what.
Therefore I prefer to ask, because I'm probably doing things in the wrong way.
Thank you.
Yes the issue is probably what you said: You have to wait until one Animation finished before starting a new one otherwise you would start a new animation every frame.
You could use a Coroutine (also check the API) to do that.
Ofcourse the same thing could be implemented also only in Update without using a Coroutine but most of the times that becomes really cluttered and sometimes even more complicated to handle. And there is not really any loss or gain (regarding performance) in simply "exporting" it to a Coroutine.
// Reference those in the Inspector or get them elsewhere
public List<AnimationClip> Clips;
public AnimationClip Idle;
private Animator _anim;
// A flag to make sure you can never start the Coroutine multiple times
private bool _isAnimating;
private void Awake()
{
_anim = GetComponent<Animator>();
}
private void Update()
{
if(Input.GetMouseButtonDown(0)
{
// To make sure there is only one routine running
if(!_isAnimating)
{
StartCoroutine(RandomAnimations());
}
}
// This would immediately interrupt the animations when mouse is not pressed anymore
// uncomment if you prefer this otherwise the Coroutine waits for the last animation to finish
// and returns to Idle state afterwards
//else if(Input.GetMouseButtonUp(0))
//{
// // Interrupts the coroutine
// StopCoroutine (RandomAnimations());
//
// // and returns to Idle state
// _anim.Play(Idle.name);
//
// // Reset flag
// _isAnimating = false;
//}
}
private IEnumerator RandomAnimations()
{
// Set flag to prevent another start of this routine
_isAnimating = true;
// Go on picking clips while mouse stays pressed
while(Input.GetMouseButton(0))
{
// Pick random clip from list
var randClip = Clips[Random.Range(0, Clips.Count)];
// Switch to the random clip
_anim.Play(randClip.name);
// Wait until clip finished before picking next one
yield return new WaitForSeconds(randClip.length);
}
// Even if MouseButton not pressed anymore waits until the last animation finished
// then returns to the Idle state
// If you rather want to interrupt immediately if the button is released
// skip this and uncomment the else part in Update
_anim.Play(Idle.name);
// Reset flag
_isAnimating = false;
}
Note that this way of randomizing does not provide things like "Don't play the same animation twice in a row" or "Play all animations before repeating one".
If you want this checkout this answer to a very similar question. There I used a randomized list to run through so there are no doubles

Time Delay for a process in Unity 3D

I have to give the delay for the process to happen, which I am calling in the Update function.
I have tried CoUpdate workaround also. Here is my code:-
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while(true)
{
yield CoUpdate();
}
}
function CoUpdate()
{
//I have placed the code of the Update().
//And called the wait function wherever needed.
}
function wait()
{
checkOnce=1; //Whenever the character is moved.
yield WaitForSeconds(2); //Delay of 2 seconds.
}
I have to move an object when a third person controller(which is another object) moves out of a boundary. I have included "yield" in my code. But, the problem happening is: The object which was moving when I gave the code for in the Update(), is moving, but isn't stopping. And it is moving up and down. I don't know what is happening! Can someone help? Please, thanks.
I am not entirely clear what you are trying to accomplish, but I can show you how to set up a Time Delay for a coroutine. For this example lets work with a simple cool down, much like you set up in your example. Assuming you want to continuously do something every 2 seconds while your game is running a slight modification can be made to your code.
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(2);
}
}
You can also calculate a wait time using some other logic
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(CalculateWait());
}
}
function CalculateWait() : float
{
// use some logic here to determine the amount of time to wait for the
// next CoStart cycle to start
return someFloat;
}
If I have missed the mark entirely then please update the question with a more detail about what you are attempting to accomplish.
I am not 100% sure that I understand you question but if you want to start one object to move when the other is out of bound then just make a reference in the first object to the second object and when the first object is out of bounds (check for this in Update of the first object) call some public function StartMove on the second object.
I wouldn't suggest CoRoutines. It can sometimes crash your computer. Just define a variable and decrement it. Example:
private float seconds = 5;
then do anywhere you want to delay:
seconds -= 1 * Time.deltaTime;
if(seconds <= 0) {your code to run}
This will make a delay of 5 seconds. You can change 5 to any value to change the number of seconds. Also you can speed up the decrement by changing the value of 1. (This is mostly useful when you want to sync 2 delayed actions, by using the same variable)
Hope this helps. Happy coding :)