Results 1 to 14 of 14
  1. #1

    Default Mulitplayer Setup

    Hello everybody! Over the last couple months I've been learning unreal script on my own through tutorials and what not and now I'm trying to test a multiplayer game. I'm not sure if there are good tutorials for setting up a MP game or how to get started. It seems like all the work I've done (regarding player models, guns, etc) may not have been done correctly, specifically I mean I have set up a game for SP use only. I currently focus on the UTGame, UTPlayerController, and UTPawn classes, but it seems as if PlayerReplicationInfo handles some MP content, whether that be the models or what not I'm not sure. Keeping that in mind these are my questions:

    1) How does the structure change when creating a SP or MP game? By structure I mean the major classes that are used and what they are primarily used for.
    2) Does setting the player and weapon models differ at all between SP and MP? I currently set them in the Pawn class, though it seems that may be incorrect.

    Any information that will potentially give me a better understanding of a MP game structure would be greatly appreciated. Also, if someone knows of a good multiplayer tutorial that would also be very helpful. Thank you in advance!!

  2. #2
    MSgt. Shooter Person
    Join Date
    Nov 2010
    Location
    Australia
    Posts
    194

    Default

    1. Not really all that much, multi player is a pretty daunting topic yet there are a few good resources around the place, I've linked to several in my recent posts. New classes are used such as the PlayerReplicationInfo class which allows you to replicate variables to every clients for doing things like making sure the client can display the current mana of the player from the server on the client's HUD.

    2. Again, not really all that much, you have to make sure that animation/model/texture changes are replicated (read up on Replication) to each client. In regards to the player, most of the replication is already done for you, things like movement prediction and rotation are dealt with in the UTGame classes. I guess the most important thing with replication is that you only need to send to the clients nothing but what absolutely has to be sent for the client's visual game (that is, if it's important to the actual game, particle effects and such don't usually need to be managed, controlled and replicated by the server) to seem like it's in sync with the server, an example: You calculate experience gain and levels, the client does not need to know about any of the process that goes into this, if you replicate the final xp and level variables through a PlayerReplicationInfo or not then each client may display that player's xp and level on their hud. The general ides is that the server is all powerful and all that the client does is roughly simulate it's own SP game based on the input transferred from the server, i.e. final player positions and velocities. Remember, don't send what doesn't need to be visually seen by the client, the client doesn't need to know about how much damage they deal unless that's to be displayed on the hud or in some other way. You can get away with keeping a surprisingly large amount of variables on the server, treat the clients as dumb rendering terminals that give input and your MP code running on the server won't be all that different from SP code.

    It is of course possible to allow the client to do processing for non-essential things like spawning a spark particle effect when a bullet hits a metal surface, you make the judgement of what you want the client to be able to do. The more a client can do, the easier it is to use hacks in the game.

    Sorry if this is a bit technical or abstract, it's good you've taken the initiative to learn coding, I taught myself and it's a very rewarding skill. I'll be happy to try and help to the best of my ability if a specific question pops up.
    ~Dulcius Ex Asperis~

  3. #3

    Default

    I really appreciate your help this is very informative. My only other concern is along the lines of an exact example. So I know I have to implement a PlayerReplicationInfo class that handles just the information I would want replicated. Where I am confused still is how do I force those updates from a different class. What I mean is, by example, I set my players model in the Pawn class. Now how do I tell my PlayerReplicationClass to update all other clients so that they can see my model?

    I am unfortunately at work which is why I haven't read the section on Replication you told me to have a look at. If its covered in there then I apologize for this post haha. I actually already tried to test a server by running it off my own computer and then making several clients log onto it just from my machine. The players locations updated correctly, but their rotation was not updating at all. Similarly, when I shoot my weapon, it would only shoot upwards (this is a top-down game, by up I mean towards the top of the screen) regardless of my models orientation. So evidently I'm doing something wrong when since you said that the players location and rotation should happen automatically. If you have any insight into my problems here that would great. Thanks again!
    Last edited by Galindar; 04-04-2012 at 01:31 PM.

  4. #4
    MSgt. Shooter Person
    Join Date
    Jul 2011
    Posts
    484

    Default

    Very easy multiplayer game you can generate in FLCT (Script Wizard) - it has a select server menu and easy UTDeathmatch game.
    You can run server by using something like this:
    <UDK path>\Binaries\UDKLift.exe server mapdm2.udk?timelimit=1

  5. #5

    Default

    Thanks for the reply!

    Well I already have a level made and my own scripts so I'm not sure if that tool applies to me? I have tested a server in a similar fashion using a shortcut of Binaries\UDK.exe and then I open the original UDK.exe and connect to it using the home IP address as a client. That tool seems very interesting though!

  6. #6
    MSgt. Shooter Person
    Join Date
    Jul 2011
    Posts
    484

    Default

    Galindar
    I'm not sure if that tool applies to me?
    You can see the generated code and do it in your program.
    Although main thing for simple multi-player game on the client side is ConsoleCommand("open 127.0.0.1"); (IP may be other, this is for localhost). And menu for select server.

  7. #7
    MSgt. Shooter Person
    Join Date
    Nov 2010
    Location
    Australia
    Posts
    194

    Default

    I think rquester is talking about how to actually host and join a server using a pre-made game, you don't really need to use that tool as it's just for learning/making a quick game from what I've gathered. One of the main problems with replication is that it is quite hard to make/find tutorials with a wide range of applications, the concepts do apply widely but the examples themselves are few and far between.

    I've linked quite a few examples and tutorials from around the place in previous posts but I don't think I've linked one that exactly refers to what you specify although I believe there was a thread I helped out in a while ago which covered the oddities that happen with cameras when replicating mainly with certain variables such as the ones that control player rotation in your top down perspective not being replicated. Sorry I can't be specific and link to all the examples, try searching for 'replication top down udk' or simply 'replication udk' on Google, there's a few good examples/tutorials around the web.
    Good luck!
    Last edited by LordOfOats; 04-05-2012 at 09:41 PM.
    ~Dulcius Ex Asperis~

  8. #8

    Default

    Alright thanks for the replies!! LordOfOats that link seems to be broken.

  9. #9

    Default

    So I've looked into this problem a bit more, and everywhere it says that just like location, rotation should update automatically without having to do anything. Because my location does update properly automatically, and it seems as if the rotation should too. Which leads me to believe I'm doing something wrong in my scripts. I'm going to include my PlayerController and Pawn classes, if you can notice for any reason why I would not be doing something correctly please let me know. I've followed several tutorials and kinda created a ad-hoc implementation which is probably why its not working.

    ThePlayerController.uc
    Code:
    class ThePlayerController extends UTPlayerController;
    
    //Camera variables
    var ControlModule ControlModule; 							//Player control module to use
    var config string DefaultControlModuleClass; 	//Default class for player control module
    
    var Vector MouseHitWorldLocation;      //Hold where the ray casted from the mouse in 3d coordinate intersect with world geometry. We will use this information for our movement target when not in pathfinding.
    var Vector MouseHitWorldNormal;        //Hold the normalized vector of world location to get direction to MouseHitWorldLocation (calculated in HUD, not used)
    var Vector MousePosWorldLocation;      //Hold deprojected mouse location in 3d world coordinates. (calculated in HUD, not used)
    var Vector MousePosWorldNormal;        //Hold deprojected mouse location normal. (calculated in HUD, used for camera ray from above)
    
    /**
     *  Calculated in Hud after mouse deprojection, uses MousePosWorldNormal as direction vector
     *  This is what calculated MouseHitWorldLocation and MouseHitWorldNormal.
     *
     *  See Hud.PostRender, Mouse deprojection needs Canvas variable.
    */
    var vector StartTrace;                 	//Hold calculated start of ray from camera
    var Vector EndTrace;                   	//Hold calculated end of ray from camera to ground
    var vector RayDir;                    	//Hold the direction for the ray query.
    var Vector PawnEyeLocation;            	//Hold location of pawn eye for rays that query if an obstacle exist to destination to pathfind.
    var Actor TraceActor;                 	//If an actor is found under mouse cursor when mouse moves, its going to end up here
    
    //Members for the custom mesh
    var SkeletalMesh defaultMesh;
    var MaterialInterface defaultMaterial0;
    var AnimTree defaultAnimTree;
    var array<AnimSet> defaultAnimSet;
    var AnimNodeSequence defaultAnimSeq;
    var PhysicsAsset defaultPhysicsAsset;
    
    simulated function PostBeginPlay()
    {
    	local class<ControlModule> ControlClass;
    	local ControlModule NewControlModule;
    
    	Super.PostBeginPlay();
    	SetBehindView(true);
    
    	ControlClass = class<ControlModule>(DynamicLoadObject(DefaultControlModuleClass, class'Class'));
       
    	if(ControlClass != none) {
    		//Associate module with PlayerController
    		NewControlModule = new(Outer) ControlClass;
    		NewControlModule.Controller = self;
    		NewControlModule.Init();
    
    		//Call active/inactive functions on new/old modules
    		if(ControlModule != none) {
    			ControlModule.OnBecomeInactive(NewControlModule);
    			NewControlModule.OnBecomeActive(ControlModule);
    		} else {
    			NewControlModule.OnBecomeActive(None);
    		}
    		
    		ControlModule = NewControlModule;
    	} else {
    		`log("Couldn't get control module class!");
    		//Not having a Control Class is fine.  PlayerController will use default controls.
    	}
       
    	resetMesh();
    }
    
    //Sets the Pawns Mesh to the resources speced in the DefaultProperties
    public function resetMesh(){
    	self.Pawn.Mesh.SetSkeletalMesh(defaultMesh);
    	self.Pawn.Mesh.SetMaterial(0,defaultMaterial0);
    	self.Pawn.Mesh.SetPhysicsAsset(defaultPhysicsAsset );
    	self.Pawn.Mesh.AnimSets = defaultAnimSet;
    	self.Pawn.Mesh.SetAnimTreeTemplate(defaultAnimTree );
    }
    
    //Exec function for switching to a different camera by class
    exec function ChangeControls( string ClassName )
    {
    	local class<ControlModule> ControlClass;
    	local ControlModule NewControlModule;
    
    	ControlClass = class<ControlModule>(DynamicLoadObject(DefaultControlModuleClass, class'Class'));
       
    	if(ControlClass != none) {
    		//Associate module with PlayerController
    		NewControlModule = new(Outer) ControlClass;
    		NewControlModule.Controller = self;
    		NewControlModule.Init();
    
    		//Call active/inactive functions on new/old modules
    		if(ControlModule != none) {
    			ControlModule.OnBecomeInactive(NewControlModule);
    			NewControlModule.OnBecomeActive(ControlModule);
    		} else {
    			NewControlModule.OnBecomeActive(None);
    		}
    
    		ControlModule = NewControlModule;
    	} else {
    		`log("Couldn't get control module class!");
    		//Not having a Control Class is fine.  PlayerController will use default controls.
    	}
    }
    
    //Exec function for switching to a different camera by class
    exec function ChangeCamera( string ClassName )
    {
    	local class<CameraModule> NewClass;
    
    	NewClass = class<CameraModule>(DynamicLoadObject(ClassName, class'Class'));
    
    	if(NewClass != none && ThePlayerCamera(PlayerCamera) != none) {
    		ThePlayerCamera(PlayerCamera).CreateCamera(NewClass);
    	}
    }
    
    event PlayerTick( float DeltaTime )
    {
    	local Vector PawnXYLocation;
    	local Rotator NewRotation;
    
        super.PlayerTick(DeltaTime);
    
    	PawnXYLocation.X = Pawn.Location.X;
    	PawnXYLocation.Y = Pawn.Location.Y;
    
    	NewRotation = Rotator(MouseHitWorldLocation - PawnXYLocation);
    	NewRotation.Pitch = 0;
    
    	Pawn.SetRotation(NewRotation);
    }
    
    //This is overridden to ensure that we use the CONTROLLER's rotation for references to movement
    //This makes our "move forward" always use the same forward, no matter what rotation our pawn is facing. It is otherwise identical to the original
    state PlayerWalking 
    {
    	function PlayerMove( float DeltaTime )
    	{
    		local vector			X,Y,Z, NewAccel;
    		local eDoubleClickDir	DoubleClickMove;
    		local rotator			OldRotation;
    		local bool				bSaveJump;
    		
    		if(Pawn == None) {
    			GotoState('Dead');
    		} else {
    			GetAxes(Rotation,X,Y,Z);
    			
    			//Update acceleration.
    			NewAccel = PlayerInput.aForward * X + PlayerInput.aStrafe * Y;
    			NewAccel.Z	= 0;
    			NewAccel = Pawn.AccelRate * Normal(NewAccel);
    
    			DoubleClickMove = PlayerInput.CheckForDoubleClickMove(DeltaTime / WorldInfo.TimeDilation);
    
    			//Update rotation.
    			OldRotation = Rotation;
    			UpdateRotation( DeltaTime );
    			Pawn.SetRotation(oldRotation - Rotation);
    			bDoubleJump = false;
    
    			if(bPressedJump && Pawn.CannotJumpNow()) {
    				bSaveJump = true;
    				bPressedJump = false;
    			} else {
    				bSaveJump = false;
    			}
    			
    			//Then save this move and replicate it
    			if(Role < ROLE_Authority) {
    				ReplicateMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation);
    			} else {
    				ProcessMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation);
    			}
    			bPressedJump = bSaveJump;
    		}
    	}
    }
    
    /**
     * Returns Player's Point of View
     * For the AI this means the Pawn's 'Eyes' ViewPoint
     * For a Human player, this means the Camera's ViewPoint
     *
     * @output	out_Location, view location of player
     * @output	out_rotation, view rotation of player
     */
    simulated event GetPlayerViewPoint( out vector out_Location, out Rotator out_Rotation )
    {
    	local Actor TheViewTarget;
    
    	//Sometimes the PlayerCamera can be none and we probably do not want this
    	//so we will check to see if we have a CameraClass.  Having a CameraClass is
    	//saying:  we want a camera so make certain one exists by spawning one
    	if(PlayerCamera == None) {
    		if(CameraClass != None) {
    			//Associate Camera with PlayerController
    			PlayerCamera = Spawn(CameraClass, Self);
    			if(PlayerCamera != None) {
    				PlayerCamera.InitializeFor( Self );
    			} else {
    				`log("Couldn't Spawn Camera Actor for Player!!");
    			}
    		}
    
    		TheViewTarget = GetViewTarget();
    		
    		if(TheViewTarget != None) {
    			out_Location = Location;
    			out_Rotation = Rotation;
    		} else {
    			out_Location = Location;
    			out_Rotation = Rotation;
    		}
    	} else {
    		PlayerCamera.GetCameraViewPoint(out_Location, out_Rotation);
    	}
    }
    
    //CheckJumpOrDuck() - Called by ProcessMove() -  Handles jump and duck buttons which are pressed
    //Overriden to do nothing and be a good boy
    function CheckJumpOrDuck() {}
    
    //Override to do nothing and be a good boy
    function ProcessViewRotation( float DeltaTime, out Rotator out_ViewRotation, Rotator DeltaRot );
    
    defaultproperties
    {
    	defaultMesh = SkeletalMesh'OF-ShipModels.raptor'
    	CameraClass = class'OmegaFrontier.ThePlayerCamera'
    	InputClass = class'OmegaFrontier.MyMouseInterfacePlayerInput'
    }
    PlayerPawn.uc
    Code:
    class PlayerPawn extends UTPawn;
    
    var bool bCanDodge;
    
    //Weapon effects use the Pawn's GetViewRotation(), so we always use the Pawn's rotation instead of the Controller's
    simulated event rotator GetViewRotation() 
    {
    	return Rotation;
    }
    
    //BecomeViewTarget - Called by Camera when this actor becomes its ViewTarget
    simulated event BecomeViewTarget( PlayerController PC )
    {
    	local PlayerController ThePC;
    
    	Super.BecomeViewTarget(PC);
    
    	ThePC = ThePlayerController(PC);
    
    	//Pawn is controlled by a UMPlayerController and has a ThePlayerCamera
        if(ThePC != none && ThePlayerCamera(ThePC.PlayerCamera) != none) {
    		//Allow custom camera to control mesh visibility, etc.
    	    ThePlayerCamera(ThePC.PlayerCamera).BecomeViewTarget(ThePC);
        }
    }
    
    /**
     *   Calculate camera view point, when viewing this pawn.
     *
     * @param   fDeltaTime   delta time seconds since last update
     * @param   out_CamLoc   Camera Location
     * @param   out_CamRot   Camera Rotation
     * @param   out_FOV      Field of View
     *
     * @return   true if Pawn should provide the camera point of view.
     */
    simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
    {
    	return false;
    }
    
    /**
     * Returns base Aim Rotation without any adjustment (no aim error, no autolock, no adhesion.. just clean initial aim rotation!)
     *
     * Called by weapon prior to firing
     * @return   base Aim rotation.
     */
    simulated singular event Rotator GetBaseAimRotation()
    {
    	local vector   POVLoc;
    	local rotator   POVRot;
    	local ThePlayerController PC;
    
    	PC = ThePlayerController(Controller);
    
    	//Pawn is controlled by a UMPlayerController and has a ThePlayerCamera
    	if(PC != none && PC.ControlModule != none) {
    		//Allow custom camera to control aim rotation, but not if we're zoomed out
    		return PC.ControlModule.GetBaseAimRotation();
    	} else {	
    		//If ThePlayerController exists and ThePlayerCamera is top down then
    		if( Controller != None && !InFreeCam() ) {
    			//Use the camera to get the players view
    			Controller.GetPlayerViewPoint(POVLoc, POVRot);
    			return POVRot;
    		
    		//Otherwise just set the Pawn rotation
    		} else {
    			POVRot = Rotation;
    			POVRot.Pitch = 0;
    			return POVRot;
    		}
    	}
    }
    
    function AddDefaultInventory()
    {
        InvManager.CreateInventory(class'OmegaFrontier.OmegaGun'); //InvManager is the pawn's InventoryManager
    }
    
    //Overriden so the player cannot dodge roll
    function bool Dodge(eDoubleClickDir DoubleClickMove)
    {
    	if(bCanDodge) {
    		return Super.Dodge(DoubleClickMove);
    	} else {
    		return false;
    	}
    }
    
    //Overriden so the player cannot fake death
    exec simulated function FeignDeath();
    
    defaultproperties
    {
    	InventoryManagerClass = class'OmegaFrontier.OmegaInventoryManager'
    
    	bCanDodge = false
    	bCanCrouch = false
    	bCanClimbLadders = false
    	bCanPickupInventory = false
    	bCanDoubleJump = false
    	bCanSwim = false
    	MaxMultiJump = 0
    	MultiJumpRemaining = 0
    	bArmsAttached = false
    	
    	bPostRenderOtherTeam = true
    	bPostRenderIfNotVisible = true
    	bPhysRigidBodyOutOfWorldCheck = true
    	bRunPhysicsWithNoController = true
    	
    	Begin Object Name=WPawnSkeletalMeshComponent
            bUpdateSkelWhenNotRendered = true
            bIgnoreControllersWhenNotRendered = false
            bTickAnimNodesWhenNotRendered = true
    		bUpdateKinematicBonesFromAnimation = true
    		AlwaysLoadOnClient = true
    		AlwaysLoadOnServer = true
    		HiddenGame = false 
    		HiddenEditor = false
    		SkeletalMesh = SkeletalMesh'OF-ShipModels.raptor'
        End Object
    	Mesh = WPawnSkeletalMeshComponent
    	Components.Add(WPawnSkeletalMeshComponent)
    }
    In ThePlayerController.uc it seems as if when DuplicateMove is called, it should be updating the location and rotation should it not? I do have a lot of things going on in these classes and they are probably inefficient so if someone has the time to look at this and help me with what I'm doing wrong I'd really appreciate it. Thanks again in advance I apologize for the continuing annoyance of this problem.

  10. #10

    Default

    Can anyone help me with this?

  11. #11

    Default

    Actually, I've been having the same problem with my game. When I turn my pawn, it only updates on my screen. Any ideas?

  12. #12

    Default

    So I got the rotation and shooting of my weapon to work properly by using the ClientSetRotation function as opposed to the SetRotation function in my PlayerTick event. My problem now is the PlayerInput is not working properly. In my game, W will always make the player move up, S will always make the player move down and so on. This is regardless of the orientation/heading of the pawns ship (its a top-down camera). But now, the PlayerInput forces the player to move relative to the direction that their facing (So W will make him move forward in the direction of his heading, and so on). Maybe I'm not setting something correctly within the PlayerController class to do with Location and Rotation but I'm not sure. If anyone has some insight into this that would be very helpful. Thanks in advance!

  13. #13

    Default

    *Update* - I think I have found the root of the problem. I know that in my PlayerMove() I use the PlayerController's rotation as opposed to the Pawn's so that this makes our "move forward" always use the same forward, no matter what rotation our pawn is facing. But since I'm using the function ClientSetRotation() for updating the rotation for the server, it updates the PC's rotation and then screws up the PlayerInput. Is there any other way I can get the Pawn's rotation to update on a server properly without the use of the ClientSetRotation? Could I call ClientSetRotation() after I duplicate my move? Any help would be appreciated! Thanks!

  14. #14

    Default

    Still stuck on this problem? Anyone got any ideas?


 

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Copyright ©2009-2011 Epic Games, Inc. All Rights Reserved.
Digital Point modules: Sphinx-based search vBulletin skin by CompletevB.com.