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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_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_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_EMBED_GAME_ON_PLAY = "embedGameOnPlay"
 | 
			
		||||
		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"
 | 
			
		||||
 | 
			
		||||
@@ -839,6 +841,13 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
 | 
			
		||||
				val enabled = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
 | 
			
		||||
				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) {
 | 
			
		||||
		gameMenuState.putBoolean(GAME_MENU_ACTION_EMBED_GAME_ON_PLAY, embedded)
 | 
			
		||||
		godot?.runOnRenderThread {
 | 
			
		||||
 
 | 
			
		||||
@@ -230,6 +230,21 @@ open class GodotGame : BaseGodotGame() {
 | 
			
		||||
		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) {
 | 
			
		||||
		val actionBundle = Bundle().apply {
 | 
			
		||||
			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.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.Button
 | 
			
		||||
import android.widget.PopupMenu
 | 
			
		||||
import android.widget.RadioButton
 | 
			
		||||
import androidx.core.content.edit
 | 
			
		||||
@@ -102,6 +103,8 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
 | 
			
		||||
		fun reset3DCamera()
 | 
			
		||||
		fun manipulateCamera(mode: CameraMode)
 | 
			
		||||
		fun muteAudio(enabled: Boolean)
 | 
			
		||||
		fun resetTimeScale()
 | 
			
		||||
		fun setTimeScale(scale: Double)
 | 
			
		||||
 | 
			
		||||
		fun isGameEmbeddingSupported(): Boolean
 | 
			
		||||
		fun embedGameOnPlay(embedded: Boolean)
 | 
			
		||||
@@ -131,6 +134,12 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
 | 
			
		||||
	private val nextFrameButton: View? by lazy {
 | 
			
		||||
		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 {
 | 
			
		||||
		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 {
 | 
			
		||||
		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{
 | 
			
		||||
			setOnCheckedChangeListener { buttonView, 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: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
 | 
			
		||||
				android:layout_width="1dp"
 | 
			
		||||
				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
 | 
			
		||||
	external fun setDebugMuteAudio(enabled: Boolean)
 | 
			
		||||
 | 
			
		||||
	@JvmStatic
 | 
			
		||||
	external fun resetTimeScale()
 | 
			
		||||
 | 
			
		||||
	@JvmStatic
 | 
			
		||||
	external fun setTimeScale(scale: Double)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns [GameEmbedMode] stored in the editor settings.
 | 
			
		||||
	 *
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user