Причина, по которой он работает только в Update
, заключается в том, что
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
нужно вызывать многократно. В противном случае он будет вращаться только на один кадр, а Time.deltaTime
приведет к очень небольшому количеству. Но событие onClick
компонента Button
вызывается только один раз. Это похоже, например, на Input.GetKeyDown
, который вызывается только один раз при нажатии клавиши. В компоненте Button
отсутствует реализация для продолжения нажатия кнопки.
Вместо этого, насколько я понимаю, вы хотите повернуть объект после нажатия кнопки
- начать вращаться навсегда
- на определенный срок
- пока вы не нажмете кнопку снова
- до тех пор, пока она не будет отпущена (-> нажмите кнопку непрерывного запуска, см. Ниже)
Один компонент Button
может выполнять только первые три:
Поворот навсегда
Либо с использованием сопрограммы
private bool isRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isRotating) return;
// start the rotation
StartCoroutine(RotateRoutine());
isRotating = true;
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
или все еще делаете это в Update
private bool isRotating = false;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void Rotate()
{
// enable the rotation
isRotating = true;
}
Обратите внимание, что решение Update
предназначено только для вашего понимания происходящего. Его не следует использовать таким образом, поскольку он не так эффективен, поскольку Update
вызывается непрерывно и проверяет также bool, если он еще не вращается. Это приводит к ненужным накладным расходам. То же самое относится к всем следующим примерам: Предпочитайте использовать сопрограммы вместо Update
( В этом случае! В других случаях лучше и эффективнее использовать один Update
метод вместо нескольких одновременных сопрограмм .. но это другая история.)
Поворот на определенную длительность
Как сопрограмма
// adjust in the inspector
// how long should rotation carry on (in seconds)?
public float duration = 1;
private bool isAlreadyRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isAlreadyRotating) return;
// start a rottaion
StartCoroutine(RotateRoutine());
}
private IEnumerator RotateRoutine()
{
// set the flag to prevent multiple callse
isAlreadyRotating = true;
float timePassed = 0.0f;
while(timePassed < duration)
{
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// add the time passed since last frame
timePassed += Time.deltaTime;
// leave here, render the frame and continue in the next frame
yield return null;
}
// reset the flag so another rotation might be started again
isAlreadyRotating = false;
}
или Update
public float duration;
private bool isRotating;
private float timer;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// reduce the timer by passed time since last frame
timer -= Time.deltaTime;
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// if the timer is not 0 return
if(timer > 0) return;
// stop rottaing
isRotating = false;
}
public void Rotate()
{
// if already rotating do nothing
if(isRotating) return;
// start rotating
isRotating = true;
// enable timer
timer = duration;
}
Переключение вращения
Это очень похоже на предыдущее, но на этот раз вместо таймера вы останавливаете вращение, нажимая снова. (Вы даже можете объединить их, но будьте осторожны, чтобы правильно сбросить флаг isRotating
;))
Как сопрограмма
private bool isRotating;
public void ToggleRotation()
{
// if rotating stop the routine otherwise start one
if(isRotating)
{
StopCoroutine(RotateRoutine());
isRotating = false;
}
else
{
StartCoroutine(RotateRoutine());
isRotating = true;
}
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
или как Update
private bool isRotating;
private void Update()
{
// if not rotating do nothing
if(!isRottaing) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void ToggleRotation()
{
// toggle the flag
isRotating = !isRotating;
}
Повернуть до отпускания
Это самая «сложная» часть, так как только Button
не может этого сделать (нет «на выпуске»). Но вы можете реализовать это, используя IPointerXHandler интерфейсы.
Хорошая новость: вы можете сохранить свой оригинальный скрипт, как он есть у вас сейчас
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
Теперь вам нужно расширение для кнопки. Он будет вызывать событие whilePressed
несколько раз в каждом кадре, например Update
, поэтому вам просто нужно ссылаться на ваш метод Rotate
в whilePressed
вместо onClick
.
Опять же, есть два варианта, либо реализовать его как сопрограмму:
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
// reference the same way as in onClick
public UnityEvent whilePressed;
private Button button;
private bool isPressed;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
// Handle pointer down
public void OnPointerDown()
{
// skip if the button is not interactable
if(!button.enabled || !button.interactable) return;
// skip if already rotating
if(isPressed) return;
StartCoroutine(PressedRoutine());
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
isPressed= false;
}
private IEnumerator RotateRoutine()
{
// repeatedly call whilePressed until button isPressed turns false
while(isPressed)
{
// break the routine if button was disabled meanwhile
if(!button.enabled || !button.interactable)
{
isPressed = false;
yield break;
}
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
// leave here, render the frame and continue in the next frame
yield return null;
}
}
}
или вы можете сделать то же самое в Update
еще раз
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
public UnityEvent whilePressed;
private bool isPressed;
private Button button;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
private void Update()
{
// if button is not interactable do nothing
if(!button.enabled || !button.interactable) return;
// if not rotating do nothing
if(!isPressed) return;
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
}
// Handle pointer down
public void OnPointerDown()
{
// enable pressed
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
// disable pressed
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
// disable pressed
isPressed= false;
}
}
Поместите этот компонент рядом с Button
компонентом. Вам не нужно ссылаться на что-либо в onClick
, просто оставьте это пустым. Вместо этого укажите что-то в onPressed
. Оставьте компонент Button
, поскольку он также обрабатывает стиль интерфейса для нас (например, при наведении курсора изменяет цвет / спрайт и т. Д.)
Опять же: решения Update
могут пока выглядеть чище / проще, но они не так эффективны (в данном случае) и просты в управлении (это может быть основано на мнении), как решения Coroutine.