public void Validate() { string message; var negativeXPosition = new XYPosition(x: -1, y: 10); var validation = new BoardConfigurationValidation(width: 100, height: 100, position: negativeXPosition, orientation: Orientation.Horizontal, size: 2); Assert.False(validation.Validate(out message)); Assert.Equal("X position '-1' cannot be negative", message); var outOfBoundXPosition = new XYPosition(x: 100, y: 10); validation = new BoardConfigurationValidation(width: 100, height: 100, position: outOfBoundXPosition, orientation: Orientation.Horizontal, size: 2); Assert.False(validation.Validate(out message)); Assert.Contains("X position '100' is outside the board width bound '100'", message); // 0-start-index var negativeYPosition = new XYPosition(x: 10, y: -1); validation = new BoardConfigurationValidation(width: 100, height: 100, position: negativeYPosition, orientation: Orientation.Horizontal, size: 2); Assert.False(validation.Validate(out message)); Assert.Equal("Y position '-1' cannot be negative", message); var outOfBoundYPosition = new XYPosition(x: 10, y: 100); validation = new BoardConfigurationValidation(width: 100, height: 100, position: outOfBoundYPosition, orientation: Orientation.Horizontal, size: 2); Assert.False(validation.Validate(out message)); Assert.Equal("Y position '100' is outside the board height bound '100'", message); // 0-start-index }
public void TestSetAndGet() { var position = new XYPosition(x: 10, y: 20); Assert.Equal(10, position.X); Assert.Equal(20, position.Y); }
public const int shieldTimeAtBirth = 5 * 1000; //出生时的盾牌时间 public static XYPosition CellToGrid(int x, int y) //求格子的中心坐标 { XYPosition ret = new XYPosition(x * numOfGridPerCell + numOfGridPerCell / 2, y * numOfGridPerCell + numOfGridPerCell / 2); return(ret); }
public void SetLaid(XYPosition pos) { if (laid) { return; } laid = true; Position = pos; shape = ShapeType.Circle; }
/// <summary> /// 在无碰撞的前提下行走最远的距离 /// </summary> /// <param name="obj">移动物体,默认obj.Rigid为true</param> /// <param name="moveVec">移动的位移向量</param> private void MoveMax(GameObject obj, Vector moveVec) { /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ XYPosition nextPos = obj.Position + Vector.Vector2XY(moveVec); uint maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); maxLen = (uint)Math.Min(maxLen, (obj.MoveSpeed / Map.Constant.numOfStepPerSecond)); obj.Move(new Vector(moveVec.angle, maxLen)); }
/// <summary> /// 碰撞检测,如果这样行走是否会与之碰撞,返回与之碰撞的物体 /// </summary> /// <param name="obj">移动的物体</param> /// <param name="moveVec">移动的位移向量</param> /// <returns>和它碰撞的物体</returns> public GameObject?CheckCollision(GameObject obj, Vector moveVec) { XYPosition nextPos = obj.Position + Vector.Vector2XY(moveVec); if (!obj.IsRigid) { if (gameMap.OutOfBound(obj)) { return(new OutOfBoundBlock(nextPos)); } return(null); } //在某列表中检查碰撞 Func <ArrayList, ReaderWriterLockSlim, GameObject> CheckCollisionInList = (ArrayList lst, ReaderWriterLockSlim listLock) => { GameObject?collisionObj = null; listLock.EnterReadLock(); try { foreach (GameObject listObj in lst) { if (WillCollide(obj, listObj, nextPos)) { collisionObj = listObj; break; } } } finally { listLock.ExitReadLock(); } return(collisionObj); }; GameObject collisionObj = null; foreach (var list in lists) { if ((collisionObj = CheckCollisionInList(list.Item1, list.Item2)) != null) { return(collisionObj); } } //如果越界,则与越界方块碰撞 if (gameMap.OutOfBound(obj)) { return(new OutOfBoundBlock(nextPos)); } return(null); }
public void TestCompare() { var equalPosition = new XYPosition(x: 10, y: 20); var notEqualXPosition = new XYPosition(x: 0, y: 20); var notEqualYPosition = new XYPosition(x: 10, y: 0); var notEqualPosition = new XYPosition(x: 0, y: 0); var position = new XYPosition(x: 10, y: 20); Assert.True(position.Equals(equalPosition)); Assert.False(position.Equals(notEqualXPosition)); Assert.False(position.Equals(notEqualYPosition)); Assert.False(position.Equals(notEqualPosition)); }
public void TestBuild_Vertical() { var position = new XYPosition(x: 50, y: 50); var verticalBattleship = BattleshipBuilderProvider.GetBuilder(Orientation.Vertical).Build(position, 3); var verticalBattleshipForTest = new XYBattleshipForTest(verticalBattleship); var xyOccupiedPositions = verticalBattleshipForTest .OccupiedPositionsForTest .Select(occupiedPosition => occupiedPosition.Position) .Cast <XYPosition>() .Select(occupiedPosition => $"{occupiedPosition.X},{occupiedPosition.Y}"); Assert.Equal(new[] { "50,50", "50,51", "50,52" }, xyOccupiedPositions.ToArray()); }
/// <summary> /// 检查obj下一步位于nextPos时是否会与listObj碰撞 /// </summary> /// <param name="obj">主动碰撞物,默认obj.Rigid为true</param> /// <param name="listObj">被动碰撞物</param> /// <param name="nextPos">obj下一步想走的位置</param> /// <returns>如果会碰撞,返回true</returns> public bool WillCollide(GameObject obj, GameObject listObj, XYPosition nextPos) { if (!listObj.IsRigid || listObj.ID == obj.ID) { return(false); //不检查自己和非刚体 } if (IgnoreCollision(obj, listObj)) { return(false); // 可以忽略碰撞 } int deltaX = Math.Abs(nextPos.x - listObj.Position.x), deltaY = Math.Abs(nextPos.y - listObj.Position.y); //默认obj是圆形的,因为能移动的物体目前只有圆形;且会移动的道具尚未被捡起,其形状没有意义,可默认为圆形 switch (listObj.Shape) { case GameObject.ShapeType.Circle: //圆与圆碰撞 { return((long)deltaX * deltaX + (long)deltaY * deltaY < ((long)obj.Radius + listObj.Radius) * ((long)obj.Radius + listObj.Radius)); } case GameObject.ShapeType.Sqare: //圆与正方形碰撞 { if (deltaX >= listObj.Radius + obj.Radius || deltaY >= listObj.Radius + obj.Radius) { return(false); } if (deltaX < listObj.Radius || deltaY < listObj.Radius) { return(true); } return((long)(deltaX - listObj.Radius) * (deltaX - listObj.Radius) + (long)(deltaY - listObj.Radius) * (deltaY - listObj.Radius) < (long)obj.Radius * (long)obj.Radius); } } return(false); }
public Wall(XYPosition initPos, int radius) : base(initPos, radius, true, 0, ObjType.Wall, ShapeType.Sqare) { }
public Amplifier(XYPosition initPos, int radius) : base(initPos, radius) { }
public Bike(XYPosition initPos, int radius) : base(initPos, radius) { }
public static int GridToCellY(XYPosition pos) //求坐标所在的格子的y坐标 { return(pos.y / numOfGridPerCell); }
public Divider(XYPosition initPos, int radius) : base(initPos, radius) { }
public Attenuator(XYPosition initPos, int radius) : base(initPos, radius) { }
public Spear(XYPosition initPos, int radius) : base(initPos, radius) { }
public Totem(XYPosition initPos, int radius) : base(initPos, radius) { }
private void ProduceOneProp() { Random r = new Random((int)Environment.TickCount64); XYPosition newPropPos = new XYPosition(); while (true) { newPropPos.x = r.Next(0, gameMap.Rows * Map.Constant.numOfGridPerCell); newPropPos.y = r.Next(0, gameMap.Cols * Map.Constant.numOfGridPerCell); int cellX = Map.Constant.GridToCellX(newPropPos), cellY = Map.Constant.GridToCellY(newPropPos); bool canLayProp = true; gameMap.ObjListLock.EnterReadLock(); try { foreach (GameObject obj in gameMap.ObjList) { if (cellX == Map.Constant.GridToCellX(obj.Position) && cellY == Map.Constant.GridToCellY(obj.Position) && (obj is Wall || obj is BirthPoint)) { canLayProp = false; break; } } } finally { gameMap.ObjListLock.ExitReadLock(); } if (canLayProp) { newPropPos = Map.Constant.CellToGrid(cellX, cellY); break; } } PropType propType = (PropType)r.Next(Prop.MinPropTypeNum, Prop.MaxPropTypeNum + 1); Prop?newProp = null; switch (propType) { case PropType.Bike: newProp = new Bike(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Amplifier: newProp = new Amplifier(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.JinKeLa: newProp = new JinKeLa(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Rice: newProp = new Rice(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Shield: newProp = new Shield(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Totem: newProp = new Totem(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Spear: newProp = new Spear(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Dirt: newProp = new Dirt(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Attenuator: newProp = new Attenuator(newPropPos, Map.Constant.unpickedPropRadius); break; case PropType.Divider: newProp = new Divider(newPropPos, Map.Constant.unpickedPropRadius); break; } if (newProp != null) { unpickedPropListLock.EnterWriteLock(); try { unpickedPropList.AddLast(newProp); } finally { unpickedPropListLock.ExitWriteLock(); } newProp.CanMove = true; } }
public BirthPoint(XYPosition initPos, int radius) : base(initPos, radius, true, 0, ObjType.BirthPoint, ShapeType.Circle) { }
public OutOfBoundBlock(XYPosition initPos) : base(initPos, int.MaxValue, true, 0, ObjType.OutOfBoundBlock, ShapeType.Sqare) { }
public long AddPlayer(PlayerInitInfo playerInitInfo) { if (!Team.teamExists(playerInitInfo.teamID) || !MapInfo.ValidBirthPointIdx(playerInitInfo.birthPointIdx) || gameMap.BirthPointList[playerInitInfo.birthPointIdx].Parent != null) { return(GameObject.invalidID); } XYPosition pos = gameMap.BirthPointList[playerInitInfo.birthPointIdx].Position; Character newPlayer = new Character(pos, Map.Constant.playerRadius, playerInitInfo.jobType, Map.Constant.basicPlayerMoveSpeed); gameMap.BirthPointList[playerInitInfo.birthPointIdx].Parent = newPlayer; gameMap.PlayerListLock.EnterWriteLock(); try { gameMap.PlayerList.Add(newPlayer); } finally { gameMap.PlayerListLock.ExitWriteLock(); } ((Team)teamList[(int)playerInitInfo.teamID]).AddPlayer(newPlayer); newPlayer.TeamID = playerInitInfo.teamID; //设置出生点的颜色 int cellX = Map.Constant.GridToCellX(pos), cellY = Map.Constant.GridToCellY(pos); gameMap.SetCellColor(cellX, cellY, Map.TeamToColor(playerInitInfo.teamID)); //开启装弹线程 new Thread ( () => { while (!gameMap.Timer.IsGaming) { Thread.Sleep(newPlayer.CD); } while (gameMap.Timer.IsGaming) { var beginTime = Environment.TickCount64; var cellX = Map.Constant.GridToCellX(newPlayer.Position); var cellY = Map.Constant.GridToCellY(newPlayer.Position); if (gameMap.GetCellColor(cellX, cellY) == Map.TeamToColor(newPlayer.TeamID)) { newPlayer.AddBulletNum(); } var endTime = Environment.TickCount64; var deltaTime = endTime - beginTime; if (deltaTime < newPlayer.CD) { Thread.Sleep(newPlayer.CD - (int)deltaTime); } else { Console.WriteLine("The computer runs so slow that the player cannot finish adding bullet during this time!!!!!!"); } } } ) { IsBackground = true }.Start(); return(newPlayer.ID); }
static void Main(string[] args) { Game game = new Game(MapInfo.defaultMap, 1); long tmpID; long[] player2ID = new long[2]; XYPosition player1Pos = Map.Constant.CellToGrid(1, 1); XYPosition player2Pos = Map.Constant.CellToGrid(2, 2); if ((tmpID = game.AddPlayer(new Game.PlayerInitInfo(0u, (JobType)0, 0))) == GameObject.invalidID) { Console.WriteLine("Add player failed!"); } else { player2ID[0] = (tmpID); } if (((tmpID) = game.AddPlayer(new Game.PlayerInitInfo(1u, (JobType)0, 0))) == GameObject.invalidID) { Console.WriteLine("Add player failed!"); } else { player2ID[1] = tmpID; } new Thread ( () => { game.StartGame(5 * 60 * 1000); } ) { IsBackground = true }.Start(); new Thread ( () => { double[] direct = new double[16]; int[] time = new int[16]; const int WKey = 0x1; const int AKey = 0x2; const int SKey = 0x4; const int DKey = 0x8; for (int i = 1; i < time.Length; ++i) { time[i] = 500; } direct[WKey] = Math.PI / 2; direct[AKey] = Math.PI; direct[SKey] = -Math.PI / 2; direct[DKey] = 0.0; direct[WKey | AKey] = Math.PI / 4 * 3; direct[WKey | DKey] = Math.PI / 4; direct[SKey | AKey] = Math.PI / 4 * 5; direct[SKey | DKey] = Math.PI / 4 * 7; while (true) { Thread.Sleep(500); int key = 0; bool WPress = Win32Api.GetKeyState((Int32)ConsoleKey.UpArrow) < 0, APress = Win32Api.GetKeyState((Int32)ConsoleKey.LeftArrow) < 0, SPress = Win32Api.GetKeyState((Int32)ConsoleKey.DownArrow) < 0, DPress = Win32Api.GetKeyState((Int32)ConsoleKey.RightArrow) < 0; if (WPress) { key |= WKey; } if (APress) { key |= AKey; } if (SPress) { key |= SKey; } if (DPress) { key |= DKey; } if (key != 0) { game.MovePlayer((long)player2ID[1], time[key], direct[key]); } } } ) { IsBackground = true }.Start(); double[] direct = new double[16]; int[] time = new int[16]; const int WKey = 0x1; const int AKey = 0x2; const int SKey = 0x4; const int DKey = 0x8; for (int i = 1; i < time.Length; ++i) { time[i] = 500; } direct[WKey] = Math.PI / 2; direct[AKey] = Math.PI; direct[SKey] = -Math.PI / 2; direct[DKey] = 0.0; direct[WKey | AKey] = Math.PI / 4 * 3; direct[WKey | DKey] = Math.PI / 4; direct[SKey | AKey] = Math.PI / 4 * 5; direct[SKey | DKey] = Math.PI / 4 * 7; while (true) { Thread.Sleep(500); int key = 0; bool WPress = Win32Api.GetKeyState((Int32)ConsoleKey.W) < 0, APress = Win32Api.GetKeyState((Int32)ConsoleKey.A) < 0, SPress = Win32Api.GetKeyState((Int32)ConsoleKey.S) < 0, DPress = Win32Api.GetKeyState((Int32)ConsoleKey.D) < 0; if (WPress) { key |= WKey; } if (APress) { key |= AKey; } if (SPress) { key |= SKey; } if (DPress) { key |= DKey; } if (key != 0) { game.MovePlayer((long)player2ID[0], time[key], direct[key]); } if (Win32Api.GetKeyState((Int32)ConsoleKey.J) < 0) { game.Attack((long)player2ID[0], 10000000, Math.PI); } } }
public JinKeLa(XYPosition initPos, int radius) : base(initPos, radius) { }
public Shield(XYPosition initPos, int radius) : base(initPos, radius) { }
/// <summary> /// 寻找最大可能移动距离 /// </summary> /// <param name="obj">移动物体,默认obj.Rigid为true</param> /// <param name="nextPos">下一步要到达的位置</param> /// <param name="moveVec">移动的位移向量,默认与nextPos协调</param> /// <returns>最大可能的移动距离</returns> public uint FindMax(GameObject obj, XYPosition nextPos, Vector moveVec) { uint maxLen = uint.MaxValue; uint tmpMax; foreach (var listWithLock in lists) { var lst = listWithLock.Item1; var listLock = listWithLock.Item2; listLock.EnterReadLock(); try { foreach (GameObject listObj in lst) { //如果再走一步发生碰撞 if (WillCollide(obj, listObj, nextPos)) { switch (listObj.Shape) //默认obj为圆形 { case GameObject.ShapeType.Circle: { //计算两者之间的距离 int orgDeltaX = listObj.Position.x - obj.Position.x; int orgDeltaY = listObj.Position.y - obj.Position.y; double mod = Math.Sqrt((long)orgDeltaX * orgDeltaX + (long)orgDeltaY * orgDeltaY); if (mod == 0.0) //如果两者重合 { tmpMax = 0; } else { Vector2 relativePosUnitVector = new Vector2(orgDeltaX / mod, orgDeltaY / mod); //相对位置的单位向量 Vector2 moveUnitVector = new Vector2(Math.Cos(moveVec.angle), Math.Sin(moveVec.angle)); //运动方向的单位向量 if (relativePosUnitVector * moveUnitVector <= 0) { continue; //如果它们的内积小于零,即反向,那么不会发生碰撞 } } double tmp = mod - obj.Radius - listObj.Radius; if (tmp <= 0) //如果它们已经贴合了,那么不能再走了 { tmpMax = 0; } else { //计算最多能走的距离 tmp = tmp / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.angle); if (tmp < 0 || tmp > uint.MaxValue || tmp == double.NaN) { tmpMax = uint.MaxValue; } else { tmpMax = (uint)tmp; } } break; } case GameObject.ShapeType.Sqare: { //如果当前已经贴合,那么不能再行走了 if (WillCollide(obj, listObj, obj.Position)) { tmpMax = 0; } else { //二分查找最大可能移动距离 int left = 0, right = (int)moveVec.length; while (left < right - 1) { int mid = (right - left) / 2 + left; if (WillCollide(obj, listObj, obj.Position + new XYPosition((int)(mid * Math.Cos(moveVec.angle)), (int)(mid * Math.Sin(moveVec.angle))))) { right = mid; } else { left = mid; } } tmpMax = (uint)left; } break; } default: tmpMax = int.MaxValue; break; } if (tmpMax < maxLen) { maxLen = tmpMax; } } } } finally { listLock.ExitReadLock(); } } return(maxLen); }