First Android Game - Part 6 July 9, 2023
This is part 6 of making my first Android Game. If you missed part5, you can find it here Time to sync up the explosions on the player when the enemy collides with the player. I will use the same concept that I did for syncing the bullets. I remove ExplosionPoolInstance.cs. In Explosion.cs I removed all the code and kept only the Start() which will tell the Explosion gameobject when it should get destroyed after it is spawned In Player.cs, in the Coroutine called HitByEnemyRoutine which is called by a PunRPC function called HitByEnemy. I would spawn the Explosions prefab I disabled the cannons and big boss so I can test the game more easily with only the potatos enemies. The enemies are not synced up so the enemies spawn position in editor and mobile are different, this goes in my favor because I can see if the explosions are synced up properly. The example is showing the editor's point of view. The mobile player is moving, if the mobile player collides with the enemy in the editor view, no explosions occur. However, if the mobile player collides with an enemy in the mobile the explosions gets synced, even though in the editor view, no enemy has collided with the mobile player. This shows the player explosions are synced up Next is to sync the explosions on the enemies. I first test on the potato enemies (not the cannon or bigboss yet). I place the potato enemies in Resources folder and attached a photonview component. Instead of doing Instantiate, I do PhotonNetwork.Instantiate. This is so the PhotonView.IsMine can be true instead of false all the time. Following how I synced up explosions with the player, when enemy colliedes with the bullet, the PunRPC function gets called The PunRPC will spawn the explosion at the location of the enemy It did not work, and instead, I spawned double the amount of enemies because PhotonNetwork.Instantiate is called on host and client because of EnemyPoolInstance.cs I decided to look at the Asteroid sample to see how they destroy asteroids when the spaceship's bullet collides with the asteroids. It turns out they approach it with DestroyGlobally if PhotonView.IsMine is true and DestroyLocally is PhotonView.IsMine is false DestroyGlobally() when call PhotonNetWork.Destroy(). While DestroyLocally() will disable the renderer The asteroid sample doesn't really tell me why my PunRPC function is not getting called. So I added a Debug.Log() in two PunRPC functions. One in Player.cs HitByEnemy() which I know is working. Another in EnemyBase.cs EnemyDestroyed() which isn't spawning explosions. I will make the one on mobile the master, and see if the Editor will print the Debug Logs when the mobile player destroys the enemy or gets hit by the enemy. Note: I have wrapped some code where the potato enemies are spawned with PhotonNetwork.IsMasterClient so only the Master will see enemies spawning. As you can see from the result the DebugLogs gets printed in Editor even when the mobile is the master. In this case, the enemies are spawned on the mobile's end and the editor is the client which has no enemies spawning. I've also mentioned the DebugLog syncing across the network here I zoomed out of the scene view and again treated the Editor as the client and the mobile as the master. Turns out, the explosions was spawning, but because I said to spawn the enemy at where the enemy is and the enemy positions are not synced up yet, so the enemy was out of the users view. As you can see the explosions are spawning! I want to sync up the potato enemies. The way they spawn is similiar to the asteroid sample, so I go back to the asteroid sample's AsteroidGameManager.cs. In StartGame() function which is called when the timer has reached contains a coroutine named SpawnAsteroid() is called only if PhotonNetwork.IsMasterClient is true. SpawnAsteroid() is also called in OnMasterClientSwitched(Player newMasterClient) which checks if the local player is the new master client. The SpawnAsteroid() function will spawn an asteroid between a min time and max time using random.range. Also notice the sample uses PhotonNetwork.InstantiateRoomObject which means that even if the original master drops out, the new master can spawn the asteroid Using the asteroid sample, I applied the same idea to tomato vs potato. I first added in TomatoGame.cs a const min time and max time that will be used to control when to spawn the enemies which are named POTATO_ENEMY_MIN_SPAWN_TIME and POTATO_ENEMY_MAX_SPAWN_TIME respectively In the Start() of EnemyPoolInstance.cs (where all the object pooling code and enums have been removed), I'll check if the user is the Master and call the SpawnSmallPotatoEnemy() coroutine The SpawnSmallPotatoEnemy() will choose a time between POTATO_ENEMY_MIN_SPAWN_TIME and POTATO_ENEMY_MAX_SPAWN_TIME, then choose a random position on the x axis between startLocationA and startLocationB. Then use PhotonNetwork.Instantiate to spawn the enemy Using the mobile as the master and editor as the client, I tested to see if the enemies are synced up. The positions are synced up, however the enemy on the client is stuttering when moving. The movement on the master is working fine. You can see the result below or see a video here Turns out the client enemy movement stuttering was due to the Update() function in EnemyBase.cs where I am moving the enemy down. I wrapped in if(PhotonNetwork.IsMasterClient) so only the master control's the enemies movement The result below shows a smooth moving enemy. You can also see the results here Next is to destroy the enemies on the master client only. We can do this by checking view.IsMine where view is PhotonView type. This works because the enemy is spawned using PhotonNetwork.Instantiate on the master. The first place to destroy the enemy is when the bullet collides with the enemy. I would do a view.IsMine check, if it is true, call PhotonNetwork.Destroy, otherwise disable the gameobject. another place where the enemy gets destroyed is where the enemy goes out of the screen. I am using a timer to time that. If view.IsMine is NOT true, break out of the coroutine, if it is true then call PhotonNetwork.Destroy The results below shows that enemies getting destroyed is synced and the the PhotonNetwork.Destroy is called only when view.IsMine is true which I've checked using Debug.Log. You can see the video version here Next is to sync the movement of the cannon enemies. Since the cannon is moved in by DoTween, is would be hard to tell if the movement is synced up properly or if the movement is synced up properly. So in Update(), there is a check for if PhotonView.IsMine, if yes then when I click with the mouse (or tap if on mobile) then make the cannon follow the mouse's horizontal position I moved the cannon enemy prefab into the resources folder, attached PhotonView component, PhotonViewTransformClassic component with Syncronize Position enabled, and dragged in explosionModified prefab into the ExplosionPrefab field on the EnemyCannon component Then making the mobile as the master and editor as the client. You can see when I click on the editor screen, the editor player is moving but the cannon enemy is not. However, when I tap on the mobile, the mobile player and cannon is moving and the editor cannon movement is being synced Using the same logic in EnabledEnemy Coroutine(), if PhotonView.IsMine is false, then break out of the coroutine. Otherwise move the cannons down. A bug was observed. When the mobile is the master and the editor is the client. The cannon enemy in both editor and mobile goes to the correct end location. However if mobile was the client and editor was the master. The mobile's cannon enemy does not go to the correct location, it appears to stop early I did a test instead of using dotween to move the cannons, I'll automatically make the cannon go to the endposition. The mobile position would be synced up even if it is the client. So it is a dotween issue I decided to implement the cannon shooting the potato enemy first to see if I'll notice the cannons not going to the correct positions later. Instead of calling the object pool to get the potato enemy prefab (which doesn't exist anymore), I call PhotonNetwork.Instantiate. I drag the potato enemy prefab into the small potato enemy field on the EnemyCannon component The recording below is from the mobile where the mobile is the client and the editor is the master. The cannon was being spawned. I also noticed that the cannon is slowly moving to the correct position on mobile. So that means the postions are being synced up but at a different rate! This calls for an investigation. See the next blog to see how I test different interpolation here
Recent blogs See all blogs