Chained Reactions (UDK)

Home / Portfolio / UDK Projects / Chained Reactions (UDK)

A physics platformer in UDK

Responsible for: (Unrealscript)

  • A form of character physics (handling a pawn as a first person character with actual physics)
  • Weapon and Gravorb colour mechanics
  • Custom triggers for custom weighted objects

This was an assignment for the start of my second year. It was created with a team of 4 members total in UDK.

The gameplay existed out of you as a character who has the ability to shift polarity of yourself and place “magnetic” grav orbs to either attract or repel you. Using these, it was the goal to slingshot yourself through the level by changing your polarity and smart placement of the orbs. The game had some other standard platformer mechanics such as buttons, pressure pads, code locks and sockets where you had to sacrifice orbs you had to continue.

The story told about a boy on his way to adulthood. His tribe is part of a post apocalyptic world where ancient technology has been reduced to the status of divine working. An ancient battlestation now functions as a proving ground for the boy and he has to reach the ends and unlock the highest tier to prove his worth to the tribe.

Problems:

  • Character Physics: the biggest problem was implementing a first person character that had actual physics. We needed these to emulate realistic forces to have the gravity shifting effect feel good and natural. I eventually fixed this somewhat by brute forcing a physicsobject attached to the pawn and throwing it around.
  • In world item placement: Placing orbs was our first goal and our first problem, it stemmed from having to do multiple raycasts to prevent an item from ending up in the wall. We created a minor algorithm that enabled us to generate a location to spawn the objects and it did include a whole set of raycasts and checks.

A sample of my pawn code can be found below:

simulated Event Tick(float Deltatime)
{
	local vector RealStartLoc;
	local vector AimDirection;
	local rotator ViewDirection;
	local rotator ArmDirection;
	local vector Hitlocation;
	local vector HitNormal;

	local vector WallOffset;
	local vector WallOffsetNormal;
	local vector TotalOffset;

	local CR_PlayerController theplayer;

	Super.Tick(Deltatime);

	CollisionComponent.SetRBRotation(m_StartRot);

	m_bPossiblePulsePadLocation = false;		

	theplayer = CR_PlayerController(GetALocalPlayerController());

	TotalOffset.X=0;
	TotalOffset.Y=0;
	TotalOffset.Z=0;

    //SpawnedProjectile = super.ProjectileFire();

	//SpawnedProjectile.SetDistance(m_Distance);
	theplayer = CR_PlayerController(GetALocalPlayerController());

	GetActorEyesViewPoint(RealStartLoc,ViewDirection);

	ArmDirection=ViewDirection;
	ArmDirection.Pitch=0;

	m_MyArm.ForceUpdate(true);
	m_MyArm.SetRotation(ArmDirection);
	m_MyArm.ForceUpdate(false);

	SetRotation(ArmDirection);
	AimDirection = Normal(Vector(ViewDirection));

	RealStartLoc+=AimDirection*32;

	if(Trace(Hitlocation,HitNormal,RealStartLoc+AimDirection*m_Distance,RealStartLoc,,,,) == none)
	{
		//tracing along Xaxis;
		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(64,0,0),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			//TotalOffset += RealStartLoc+AimDirection*m_Distance - WallOffset;
			TotalOffset += Vect(64,0,0) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
			//volgens grafiek: vect(32,0,0) - (Walloffset - (RealstartLoc+Aimdirection*m_Distance);
		}

		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(-64,0,0),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			TotalOffset += Vect(-64,0,0) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
		}

		//tracing along Yaxis
		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(0,64,0),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			TotalOffset += Vect(0,64,0) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
		}

		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(0,-64,0),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			TotalOffset += Vect(0,-64,0) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
		}

		//tracing along Zaxis
		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(0,0,64),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			TotalOffset += Vect(0,0,64) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
		}

		if(Trace(WallOffset,HitNormal,RealStartLoc+AimDirection*m_Distance + Vect(0,0,-64),RealStartLoc+AimDirection*m_Distance,,,,) != none){
			TotalOffset += Vect(0,0,-64) - (WallOffset - (RealStartLoc+AimDirection*m_Distance));
			m_bPossiblePulsePadLocation= true;
		}

		Hitlocation = RealStartLoc+AimDirection*m_Distance;

	}
	else
	{
		if(HitNormal.X < 0)
			TotalOffset += Vect(64,0,0);
		if(HitNormal.X > 0)
			TotalOffset += Vect(-64,0,0);
		if(HitNormal.Y < 0)
			TotalOffset += Vect(0,64,0);
		if(HitNormal.Y > 0)
			TotalOffset += Vect(0,-64,0);
		if(HitNormal.Z < 0)
			TotalOffset += Vect(0,0,64);
		if(HitNormal.Z > 0){
			m_bPossiblePulsePadLocation= true;
			TotalOffset += Vect(0,0,-64);
		}
		theplayer.MoveTarget(Hitlocation - TotalOffset);
	}

	Hitlocation.X = Hitlocation.X - TotalOffset.X -(Hitlocation.X - TotalOffset.X)%32;
	Hitlocation.Y = Hitlocation.Y - TotalOffset.Y -(Hitlocation.Y - TotalOffset.Y)%32;
	Hitlocation.Z = Hitlocation.Z - TotalOffset.Z -(Hitlocation.Z - TotalOffset.Z)%32;

	m_TargetLocation=Hitlocation;

	theplayer.MoveTarget(Hitlocation);

}