You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Merge pull request #111296 from syntaxerror247/speed-control
Android Editor: Add game speed control options in game menu bar
This commit is contained in:
@@ -142,4 +142,22 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_set
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetTimeScale(JNIEnv *env, jclass clazz) {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||||
|
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||||
|
game_view_plugin->get_debugger()->reset_time_scale();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setTimeScale(JNIEnv *env, jclass clazz, jdouble scale) {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
|
||||||
|
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
|
||||||
|
game_view_plugin->get_debugger()->set_time_scale(scale);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,4 +44,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_res
|
|||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetTimeScale(JNIEnv *env, jclass clazz);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setTimeScale(JNIEnv *env, jclass clazz, jdouble scale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||||||
internal const val GAME_MENU_ACTION_RESET_CAMERA_3D_POSITION = "resetCamera3DPosition"
|
internal const val GAME_MENU_ACTION_RESET_CAMERA_3D_POSITION = "resetCamera3DPosition"
|
||||||
internal const val GAME_MENU_ACTION_EMBED_GAME_ON_PLAY = "embedGameOnPlay"
|
internal const val GAME_MENU_ACTION_EMBED_GAME_ON_PLAY = "embedGameOnPlay"
|
||||||
internal const val GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO = "setDebugMuteAudio"
|
internal const val GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO = "setDebugMuteAudio"
|
||||||
|
internal const val GAME_MENU_ACTION_RESET_TIME_SCALE = "resetTimeScale"
|
||||||
|
internal const val GAME_MENU_ACTION_SET_TIME_SCALE = "setTimeScale"
|
||||||
|
|
||||||
private const val GAME_WORKSPACE = "Game"
|
private const val GAME_WORKSPACE = "Game"
|
||||||
|
|
||||||
@@ -839,6 +841,13 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||||||
val enabled = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
|
val enabled = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
|
||||||
muteAudio(enabled)
|
muteAudio(enabled)
|
||||||
}
|
}
|
||||||
|
GAME_MENU_ACTION_RESET_TIME_SCALE -> {
|
||||||
|
resetTimeScale()
|
||||||
|
}
|
||||||
|
GAME_MENU_ACTION_SET_TIME_SCALE -> {
|
||||||
|
val scale = actionData.getDouble(KEY_GAME_MENU_ACTION_PARAM1)
|
||||||
|
setTimeScale(scale)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -909,6 +918,20 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun resetTimeScale() {
|
||||||
|
gameMenuState.putDouble(GAME_MENU_ACTION_SET_TIME_SCALE, 1.0)
|
||||||
|
godot?.runOnRenderThread {
|
||||||
|
GameMenuUtils.resetTimeScale()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTimeScale(scale: Double) {
|
||||||
|
gameMenuState.putDouble(GAME_MENU_ACTION_SET_TIME_SCALE, scale)
|
||||||
|
godot?.runOnRenderThread {
|
||||||
|
GameMenuUtils.setTimeScale(scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun embedGameOnPlay(embedded: Boolean) {
|
override fun embedGameOnPlay(embedded: Boolean) {
|
||||||
gameMenuState.putBoolean(GAME_MENU_ACTION_EMBED_GAME_ON_PLAY, embedded)
|
gameMenuState.putBoolean(GAME_MENU_ACTION_EMBED_GAME_ON_PLAY, embedded)
|
||||||
godot?.runOnRenderThread {
|
godot?.runOnRenderThread {
|
||||||
|
|||||||
@@ -230,6 +230,21 @@ open class GodotGame : BaseGodotGame() {
|
|||||||
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun resetTimeScale() {
|
||||||
|
val actionBundle = Bundle().apply {
|
||||||
|
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_RESET_TIME_SCALE)
|
||||||
|
}
|
||||||
|
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTimeScale(scale: Double) {
|
||||||
|
val actionBundle = Bundle().apply {
|
||||||
|
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_SET_TIME_SCALE)
|
||||||
|
putDouble(KEY_GAME_MENU_ACTION_PARAM1, scale)
|
||||||
|
}
|
||||||
|
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
|
||||||
|
}
|
||||||
|
|
||||||
override fun embedGameOnPlay(embedded: Boolean) {
|
override fun embedGameOnPlay(embedded: Boolean) {
|
||||||
val actionBundle = Bundle().apply {
|
val actionBundle = Bundle().apply {
|
||||||
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_EMBED_GAME_ON_PLAY)
|
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_EMBED_GAME_ON_PLAY)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
@@ -102,6 +103,8 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||||||
fun reset3DCamera()
|
fun reset3DCamera()
|
||||||
fun manipulateCamera(mode: CameraMode)
|
fun manipulateCamera(mode: CameraMode)
|
||||||
fun muteAudio(enabled: Boolean)
|
fun muteAudio(enabled: Boolean)
|
||||||
|
fun resetTimeScale()
|
||||||
|
fun setTimeScale(scale: Double)
|
||||||
|
|
||||||
fun isGameEmbeddingSupported(): Boolean
|
fun isGameEmbeddingSupported(): Boolean
|
||||||
fun embedGameOnPlay(embedded: Boolean)
|
fun embedGameOnPlay(embedded: Boolean)
|
||||||
@@ -131,6 +134,12 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||||||
private val nextFrameButton: View? by lazy {
|
private val nextFrameButton: View? by lazy {
|
||||||
view?.findViewById(R.id.game_menu_next_frame_button)
|
view?.findViewById(R.id.game_menu_next_frame_button)
|
||||||
}
|
}
|
||||||
|
private val setTimeScaleButton: Button? by lazy {
|
||||||
|
view?.findViewById<Button>(R.id.game_menu_set_time_scale_button)
|
||||||
|
}
|
||||||
|
private val resetTimeScaleButton: View? by lazy {
|
||||||
|
view?.findViewById(R.id.game_menu_reset_time_scale_button)
|
||||||
|
}
|
||||||
private val unselectNodesButton: RadioButton? by lazy {
|
private val unselectNodesButton: RadioButton? by lazy {
|
||||||
view?.findViewById(R.id.game_menu_unselect_nodes_button)
|
view?.findViewById(R.id.game_menu_unselect_nodes_button)
|
||||||
}
|
}
|
||||||
@@ -179,6 +188,33 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val timeScaleMenu: PopupMenu by lazy {
|
||||||
|
PopupMenu(context, setTimeScaleButton).apply {
|
||||||
|
inflate(R.menu.time_scale_options)
|
||||||
|
setOnMenuItemClickListener { menuItem: MenuItem ->
|
||||||
|
val selectedScale = when (menuItem.itemId) {
|
||||||
|
R.id.speed_1_16 -> 0.0625f
|
||||||
|
R.id.speed_1_8 -> 0.125f
|
||||||
|
R.id.speed_1_4 -> 0.25f
|
||||||
|
R.id.speed_1_2 -> 0.5f
|
||||||
|
R.id.speed_3_4 -> 0.75f
|
||||||
|
R.id.speed_1_0 -> 1.0f
|
||||||
|
R.id.speed_1_25 -> 1.25f
|
||||||
|
R.id.speed_1_5 -> 1.5f
|
||||||
|
R.id.speed_1_75 -> 1.75f
|
||||||
|
R.id.speed_2_0 -> 2.0f
|
||||||
|
R.id.speed_4_0 -> 4.0f
|
||||||
|
R.id.speed_8_0 -> 8.0f
|
||||||
|
R.id.speed_16_0 -> 16.0f
|
||||||
|
else -> 1.0f
|
||||||
|
}
|
||||||
|
setTimeScaleButton?.text = menuItem.title
|
||||||
|
menuListener?.setTimeScale(selectedScale.toDouble())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val menuItemActionView: View by lazy {
|
private val menuItemActionView: View by lazy {
|
||||||
View(context)
|
View(context)
|
||||||
}
|
}
|
||||||
@@ -280,6 +316,19 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeScaleButton?.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
timeScaleMenu.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTimeScaleButton?.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
menuListener?.resetTimeScale()
|
||||||
|
setTimeScaleButton?.text = "1.0x"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unselectNodesButton?.apply{
|
unselectNodesButton?.apply{
|
||||||
setOnCheckedChangeListener { buttonView, isChecked ->
|
setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:pathData="M5,8a4,4 0,1 1,4 4v2a6,6 0,1 0,-6 -6H1l3,4 3,-4z"
|
||||||
|
android:fillColor="@color/game_menu_icons_color_state"/>
|
||||||
|
</vector>
|
||||||
@@ -33,6 +33,22 @@
|
|||||||
android:background="@drawable/game_menu_button_bg"
|
android:background="@drawable/game_menu_button_bg"
|
||||||
android:src="@drawable/next_frame" />
|
android:src="@drawable/next_frame" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/game_menu_set_time_scale_button"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/game_menu_button_bg"
|
||||||
|
android:text="1.0x"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/game_menu_reset_time_scale_button"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/game_menu_button_bg"
|
||||||
|
android:src="@drawable/reset" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/speed_1_16" android:title="1/16x" />
|
||||||
|
<item android:id="@+id/speed_1_8" android:title="1/8x" />
|
||||||
|
<item android:id="@+id/speed_1_4" android:title="1/4x" />
|
||||||
|
<item android:id="@+id/speed_1_2" android:title="1/2x" />
|
||||||
|
<item android:id="@+id/speed_3_4" android:title="3/4x" />
|
||||||
|
<item android:id="@+id/speed_1_0" android:title="1.0x" />
|
||||||
|
<item android:id="@+id/speed_1_25" android:title="1.25x" />
|
||||||
|
<item android:id="@+id/speed_1_5" android:title="1.5x" />
|
||||||
|
<item android:id="@+id/speed_1_75" android:title="1.75x" />
|
||||||
|
<item android:id="@+id/speed_2_0" android:title="2.0x" />
|
||||||
|
<item android:id="@+id/speed_4_0" android:title="4.0x" />
|
||||||
|
<item android:id="@+id/speed_8_0" android:title="8.0x" />
|
||||||
|
<item android:id="@+id/speed_16_0" android:title="16.0x" />
|
||||||
|
</menu>
|
||||||
@@ -95,6 +95,13 @@ object GameMenuUtils {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun setDebugMuteAudio(enabled: Boolean)
|
external fun setDebugMuteAudio(enabled: Boolean)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun resetTimeScale()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
external fun setTimeScale(scale: Double)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns [GameEmbedMode] stored in the editor settings.
|
* Returns [GameEmbedMode] stored in the editor settings.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user