// Update is called once per frame void Update() { switch (estado) { case EstadoDaqui.preparandoTiro: transform.rotation = Quaternion.Lerp( transform.rotation, Rotation2D.GetRotation(transform.position, doHeroi.position), 10 * Time.deltaTime); tempoDecorrido += Time.deltaTime; if (tempoDecorrido > intervaloDeTiro) { estado = EstadoDaqui.emTiro; GameObject G = InstanciaLigando.Instantiate(projetil, transform.position, 10); G.AddComponent <ProjetilQuicavel>().Iniciar(transform.right, particulaTelegrafista, velDoProjetil); InstanciaLigando.Instantiate(particulaTelegrafista, transform.position, 5); VerificadorDeDistancia(); } else if (Vector2.Distance(transform.position, doHeroi.position) > distanciaDeAparecer || Vector2.Distance(transform.position, doHeroi.position) < proximoDeMais) { estado = EstadoDaqui.emEspera; ShowHideEnemy(false); VerificadorDeDistancia(); } break; } }
public void TestObject() { Quaternion2D q1 = new Quaternion2D(2, 4); Quaternion2D q2 = new Quaternion2D(3, 5); Assert.AreEqual(q1.GetHashCode(), (new Quaternion2D(2, 4)).GetHashCode()); Assert.AreNotEqual(q1.GetHashCode(), q2.GetHashCode()); object o = new object(); Assert.AreNotEqual(q1, o); o = q1; Assert.AreEqual(q1, o); o = q2; Assert.AreNotEqual(q1, o); StringAssert.AreEqualIgnoringCase(q1.ToString(), "(2, 4)"); StringAssert.AreNotEqualIgnoringCase(q1.ToString(), q2.ToString()); Quaternion2D q = new Quaternion2D(); q.angle = Mathf.PI * 0.3f; Rotation2D rot = new Rotation2D(Mathf.PI * 0.3f); Assert.AreEqual(q * Vector2.right, rot.x); }
public void Scene_FluentBuildTransformations_ShouldAdded() { var rotationCenterX = 10.0; var rotationCenterY = 15.0; var rotationAngle = Math.PI / 2; var shapes1 = CreateShapes(); var shapes2 = CreateShapes(); //Old syntax: method to method var translation1 = new Translation2D(rotationCenterX, rotationCenterY); var rotation = new Rotation2D(rotationAngle); var translation2 = new Translation2D(-rotationCenterX, -rotationCenterY); var transformation = Transformation2D.Compose(translation1, rotation, translation2); for (int i = 0; i < shapes1.Count; i++) { shapes1[i].Apply(transformation); shapes1[i].DoTransform(); } //New syntax: fluent builder shapes2.Translate(rotationCenterX, rotationCenterY).Rotate(rotationAngle).Translate(-rotationCenterX, -rotationCenterY).DoTransform(); shapes1.Shapes.Should().BeEquivalentTo(shapes2.Shapes); }
public void Execute(Entity entity, int index, [ReadOnly] ref AntSeed seed, [ReadOnly] ref MapSize mapSize) { for (int i = 0; i < seed.Count; i++) { var minPos = new float2(-5f, -5f) + mapSize.Value * 0.5f; var maxPos = new float2(5f, 5f) + mapSize.Value * 0.5f; var holdingResources = new HoldingResources(); holdingResources.Value = false; var translation = new Translation2D(); translation.Value = this.Random.NextFloat2(minPos, maxPos); var rotation = new Rotation2D(); rotation.Value = this.Random.NextFloat() * PI * 2f; var speed = new Velocity(); speed.Value = seed.InitialSpeed; var stats = new AntStats(); stats.Steering = seed.RandomSteering; stats.Acceleration = seed.Acceleration; stats.WallSteerStrength = seed.WallSteerStrength; stats.GoalSteerStrength = seed.GoalSteerStrength; stats.Brightness = this.Random.NextFloat(.75f, 1.25f); var ant = this.Buffer.Instantiate(seed.AntPrefab); this.Buffer.SetComponent(ant, holdingResources); this.Buffer.SetComponent(ant, translation); this.Buffer.SetComponent(ant, rotation); this.Buffer.SetComponent(ant, speed); this.Buffer.SetComponent(ant, stats); } this.Buffer.RemoveComponent <AntSeed>(entity); }
private Room CreateRoomFromStack(Property.RoomFilter filter, bool specified, IntVector3 position, IntVector3 entry) { if (specified) { Debug.LogFormat("create room at {0},{1},{2}", position.x, position.y, position.z); Room result = null; if (dicRoomList.TryGetValue(position, out result) && result != null) { return(null); } } if (roomQueue.Count == 0) { RefillRoomQueue(15); } Property.Room roomProp = null; bool isRoomGood; int count = roomQueue.Count; do { if (--count < 0) { break; } roomProp = roomQueue.Dequeue(); isRoomGood = filter == null || filter.Check(roomProp); isRoomGood = isRoomGood && (specified ? roomProp.CanPlaceAt(position) : FindPlace(roomProp, out position, out entry)); if (!isRoomGood) { roomQueue.Enqueue(roomProp); roomProp = null; } } while(!isRoomGood); Room roomInst = null; if (roomProp != null) { roomInst = new Room(roomProp); //make sure room has a connector to entry Rotation2D roomRot = roomInst.FaceTo(entry - position); //TODO: after we have position & rotation, check if this room can be placed in scene roomInst.Init(position, roomRot, Vector3.zero, Quaternion.identity); } return(roomInst); }
public ActorInstance.Room CreateLinkedRoom(IntVector3 logicPos, Rotation2D rot, Vector3 worldPos, Quaternion worldRot) { //Assuming there's no room-conflict ActorInstance.Room linkRoom = GameLoader.Instance.GetRoomByLogicPosition(logicPos + LogicPosition); if (linkRoom == null) { Room roomProp = Resources.Load <Room>(LinkRoomPath); if (roomProp != null) { ActorInstance.Room instRoom = new ActorInstance.Room(roomProp); instRoom.Init(logicPos + rot * LogicPosition, rot, worldPos + Position, worldRot * Quaternion.Euler(EulerRotation)); linkRoom = instRoom; } } return(linkRoom); }
void SpawnarMagia(Vector3 pos, Vector3 dir = default(Vector3)) { if (dir == default(Vector3)) { dir = DirecaoNoPlano.NoUpNormalizado(transform.position, HeroPosition); } GameObject G = InstanciaLigando.Instantiate(setaSombria, pos, 10); EventAgregator.Publish(new StandardSendGameEvent(EventKey.disparaSom, SoundEffectID.lancaProjetilInimigo)); G.AddComponent <ProjetilInimigo>().IniciarProjetilInimigo(dir, particulaDoTeleport, velDaMagia, SoundEffectID.lancaProjetilInimigo); G.transform.rotation = Rotation2D.GetRotation(dir); }
public Rotation2D FaceTo(IntVector3 entry /*relative position*/) { Rotation2D rot = TurnRot(entry, false); if (ConnectorList != null) { foreach (Connector conn in ConnectorList) { ConnectorSocket socket = conn.socket as ConnectorSocket; if (socket.socketType == ConnectorSocket.TYPE.TYPE_RELATIVE && conn.Enterable()) { rot *= TurnRot(socket.ConnectPos, true); return(rot); } } } return(Rotation2D.Identity); }
public void Init(IntVector3 logicPos, Rotation2D rot, Vector3 worldPos, Quaternion worldRot) { if (bInit) { LogicPosition = logicPos; LogicRotation = rot; SetTransform(worldPos, worldRot); return; } LogicPosition = logicPos; LogicRotation = rot; SetTransform(worldPos, Quaternion.identity); bInit = true; GameLoader.Instance.RegisterRoom(this); if (ConnectorList != null) { foreach (Connector connector in ConnectorList) { GameLoader.Instance.ConnectToWorld(connector); } } if (RoomProp.GroupRooms != null) { foreach (var roomLink in RoomProp.GroupRooms) { if (LinkRooms == null) { LinkRooms = new List <Room>(); } Room room = roomLink.CreateLinkedRoom(LogicPosition, LogicRotation, worldPos, worldRot); LinkRooms.Add(room); } } PostRoomCreated(); }
protected void _UpdateSceneRegion() { // partially based on c++ t2dSceneWindow::setCurrentCameraPosition...doesn't do zoom window or mounting // resize base on aspect ratio if specified _extent = ApplyResize(_extent); // set scene range. // if we implement zooming this will need to change, see t2dSceneWindow::calculateCameraView. Vector2 centerPos = CenterPosition; if (Rotation == 0.0f) { _sceneMin.X = centerPos.X - _extent.X / (2 * _zoom); _sceneMin.Y = centerPos.Y - _extent.Y / (2 * _zoom); _sceneMax.X = centerPos.X + _extent.X / (2 * _zoom); _sceneMax.Y = centerPos.Y + _extent.Y / (2 * _zoom); _camToWorld = Matrix.Identity * Matrix.CreateScale(1 / _zoom); } else { Rotation2D rot = new Rotation2D(MathHelper.ToRadians(Rotation)); Vector2 x = rot.X; Vector2 y = rot.Y; float xext = 0.5f * _extent.X * Math.Abs(x.X) + 0.5f * _extent.Y * Math.Abs(y.X); float yext = 0.5f * _extent.X * Math.Abs(x.Y) + 0.5f * _extent.Y * Math.Abs(y.Y); _sceneMin = _sceneMax = centerPos; _sceneMin.X -= xext * (1 / _zoom); _sceneMax.X += xext * (1 / _zoom); _sceneMin.Y -= yext * (1 / _zoom); _sceneMax.Y += yext * (1 / _zoom); _camToWorld = Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)) * Matrix.CreateScale(1 / _zoom); } _camToWorld.Translation = new Vector3(centerPos, 20.0f); }
/// <summary> /// Fire a tank shell. /// </summary> void _Fire() { T2DSceneObject proj = (T2DSceneObject)_projectileTemplate.Clone(); proj.Rotation = SceneObject.Rotation; // Allow the projectile to blow up. CombustibleComponent cc = proj.Components.FindComponent<CombustibleComponent>(); if (cc != null) { cc.ParentObject = SceneObject; // Assign a sound index to this projectile. cc.ParentSoundIndex = _nextFreeProjectileSound; } // Remove the firing player from the projectile's collides with // list and add AI players proj.Collision.CollidesWith -= _projectileDoNotHitType; //proj.Collision.CollidesWith += GameData.Instance.AIPlayerObjectType; // Calculate a firing solution Vector2 firepos = new Vector2(0.0f, -32.5f); Rotation2D rot = new Rotation2D(MathHelper.ToRadians(SceneObject.Rotation)); firepos = rot.Rotate(firepos); proj.Position = SceneObject.Position + firepos; Vector2 vel = new Vector2(); vel.X = (float)System.Math.Sin((double)MathHelper.ToRadians(-proj.Rotation)) * -_projectileVelocity; vel.Y = (float)System.Math.Cos((double)MathHelper.ToRadians(proj.Rotation)) * -_projectileVelocity; proj.Physics.Velocity = vel; Owner.Manager.Register(proj); // Apply an impulse to the firing player Vector2 impulse = new Vector2(); impulse.X = (float)System.Math.Sin((double)MathHelper.ToRadians( -SceneObject.Rotation + 180.0f)) * -_projectileKickback; impulse.Y = (float)System.Math.Cos((double)MathHelper.ToRadians( SceneObject.Rotation - 180.0f)) * -_projectileKickback; SceneObject.Physics.ApplyImpulse(impulse); // Apply game pad vibration to the firing player if appropriate T2DStaticSprite psprite = SceneObject as T2DStaticSprite; if (psprite != null) { GamepadVibrationComponent vib = psprite.Components.FindComponent<GamepadVibrationComponent>(); if (vib != null) { vib.SetHighSpeedVibration(0.1f, 0.5f); } } // Play muzzle flash particles T2DParticleEffect flash = SceneObject.GetMountedObject("muzzle") as T2DParticleEffect; if (flash != null) { flash.PlayEffect(false); } // Play sounds. Projectile whistle sounds are buffered on us to // allow them to fade when the projectile is destroyed. Otherwise // the sound would just stop. MindcraftersComponentsLibrary.SoundBank.PlayCue("fireShell"); if (_projectileSounds[_nextFreeProjectileSound] != null && _projectileSounds[_nextFreeProjectileSound].IsPlaying) _projectileSounds[_nextFreeProjectileSound].Stop( AudioStopOptions.Immediate); _projectileSounds[_nextFreeProjectileSound] = MindcraftersComponentsLibrary.SoundBank.GetCue("shellWhistle"); _projectileSounds[_nextFreeProjectileSound].Play(); _nextFreeProjectileSound++; if (_nextFreeProjectileSound >= _maxProjectileSounds) _nextFreeProjectileSound = 0; //TODO: update shots fired statistic _countdownToFire = FireRate; }
void DoSpawn(SpriteSpawnerTest.SpawnData spawner, Material mat) { ComponentType[] type = new ComponentType[] { typeof(Position2D), typeof(Rotation2D), typeof(Scale), typeof(SpriteSheetAnimation), typeof(SpriteSheetMaterial), typeof(SpriteSheetColor), typeof(UVCell) }; var em = EntityManager; var archetype = em.CreateArchetype(type); NativeArray <Entity> entities = new NativeArray <Entity>(spawner.spriteCount, Allocator.Temp); em.CreateEntity(archetype, entities); int cellCount = CachedUVData.GetCellCount(mat); Random rand = new Random((uint)UnityEngine.Random.Range(0, int.MaxValue)); Rect area = spawner.GetSpawnArea(); SpriteSheetMaterial material = new SpriteSheetMaterial { material = mat }; var maxFrames = CachedUVData.GetCellCount(mat); //Debug.Log("Spawning entities and setting components"); for (int i = 0; i < entities.Length; i++) { Entity e = entities[i]; Scale scale = new Scale { Value = rand.NextFloat(spawner.minScale, spawner.maxScale) }; float2 p = rand.NextFloat2(area.min, area.max); Position2D pos = spawner.origin.xy + p; Rotation2D rot = new Rotation2D { angle = spawner.rotation }; int numFrames = rand.NextInt(3, maxFrames / 2); int minFrame = rand.NextInt(0, maxFrames - numFrames); SpriteSheetAnimation anim = new SpriteSheetAnimation { play = true, repetition = SpriteSheetAnimation.RepetitionType.Loop, fps = rand.NextFloat(spawner.minFPS_, spawner.maxFPS_), frameMin = minFrame, frameMax = minFrame + numFrames }; SpriteSheetColor color = UnityEngine.Random.ColorHSV(.35f, .75f); UVCell cell = new UVCell { value = rand.NextInt(0, maxFrames) }; em.SetComponentData(e, scale); em.SetComponentData(e, pos); em.SetComponentData(e, anim); em.SetComponentData(e, color); em.SetComponentData(e, cell); em.SetComponentData(e, rot); em.SetSharedComponentData(e, material); } }
/* adds a single new item on the map. if */ private FlashGlanceRoundItemVO AddItem() { if (_allItems.Count >= MaxItems) { return(null); //map is full } SafeHashCodePoint emptyPosition = GetEmptyPosition(); FlashGlanceRoundItemVO item = null; if (emptyPosition != null) { _freePositions.Remove(emptyPosition); int rotation = (_random.NextDouble() <= _castedConfig.GetRotationProbabilityByLevel(CurrentDifficulty)) ? Rotation2D.GetRandom(false) : Rotation2D.DEGREE_0; float scale = (float)((_random.NextDouble() <= _castedConfig.GetScalingProbabilityByLevel(CurrentDifficulty)) ? _random.NextDouble() * 0.3f + 0.7f : 1f); item = new FlashGlanceRoundItemVO(_lastItemId, _lastCypher, emptyPosition, rotation, scale, _colors[_colorIndex]); _allItems.Add(item); _solutionChain.Add(item); item.IsBusy = true; _lastItemId++; //generate next cypher string formula = _castedConfig.GetNextItemFormulaByLevel(CurrentDifficulty); Expression exp = _expressionParser.EvaluateExpression(formula); if (exp.Parameters.ContainsKey("x")) { exp.Parameters["x"].Value = _lastCypher; // set the named parameter "x" } else { exp.Parameters.Add("x", new Parameter("x") { Value = _lastCypher }); } int formulaResult = (int)exp.Value; _lastCypher = (formulaResult > 0) ? formulaResult : _lastCypher + 1; } //_logger.LogMessage(LogLevel.Informational, "New item created: " + item.ToString()); return(item); }
static bool _CheckIntervalIntersection(Vector2[] srcVertexList, Vector2[] dstVertexList, ref Vector2 vertexAxis, ref Vector2 refLocalOffset, ref Vector2 refLocalVelocity, ref Rotation2D refLocalRot, ref float timeAxis, float collisionTime) { // calculate intervals for source/destination Vector2 vertexAxisRotated = refLocalRot.Unrotate(vertexAxis); // Get the interval of the polygons projected onto the axis. Min returned in x, max in y. Vector2 srcProj = _CalculateInterval(srcVertexList, ref vertexAxisRotated); Vector2 dstProj = _CalculateInterval(dstVertexList, ref vertexAxis); // add reference offset float srcOffset = Vector2.Dot(refLocalOffset, vertexAxis); srcProj += new Vector2(srcOffset, srcOffset); // calculate intervals // Calculate delta of min of one interval with max of the other. // Note that if the intervals overlap then both deltas must be negative. // I.e., delta>0 --> intervals don't overlap because one min greater than // the other max. float delta0 = srcProj.X - dstProj.Y; float delta1 = dstProj.X - srcProj.Y; // Are we seperated? if (delta0 > 0.0f || delta1 > 0.0f) { // Yes, so test the dynamic intervals // calculate speed along a particular axis float speed = Vector2.Dot(refLocalVelocity, vertexAxis); float epsilon = 0.0001f; // ignore small speeds if (Math.Abs(speed) < epsilon) return false; // The smallest absolute value must be the separation. The reason is that one // delta measures separation between intervals, the other measures the span of // the two intervals (we want separation, which is always smaller). Convert separation // into time by dividing by the speed (adjust sign depending on whether we are going // from source to dest or not). timeAxis = delta0 * delta0 < delta1 * delta1 ? -delta0 / speed : delta1 / speed; // successful collision? return timeAxis >= 0.0f && timeAxis <= collisionTime; } else { // No, so we're overlapped. // // Let's get the interval of the smallest |delta0| and |delta1| and // encode it as a negative value to signify an overlap rather than // a disjoint collision in forward-time. timeAxis = (delta0 > delta1) ? delta0 : delta1; // Successful Collision! return true; } }
/// <summary> /// Render all the particles in the emitter. This method is called by particle effects /// and should not normally be called otherwise. /// </summary> /// <param name="vertexScratch">Scratch space for vertices. Must contain enough space for all particles to be rendered.</param> /// <param name="vertexOffset">Offset into vertex scratch space for first vertex. Will be udpated for later particles.</param> /// <param name="textureCoords">Array of texture coords.</param> /// <param name="normal">Precomputed normal for particle.</param> /// <param name="tangent">Precomputed tangent for particle.</param> public void RenderEmitter(GFXVertexFormat.PCTTBN[] vertexScratch, ref int vertexOffset, Vector2[] textureCoords, ref Vector4 normal, ref Vector4 tangent) { // Any emitter-data or Allocated Particles? if (CurrentEmitterData == null || Allocated == 0) return; // Sanity! Assert.Fatal(Head != T2DParticleManager.NodeEndMarker, "Particles allocated but none in emitter chain!"); // Fetch Particle Manager Pool. T2DParticle[] particleManagerPool = T2DParticleManager.Instance.Pool; // Fetch Particle Count. int particleCount = Allocated; // Note Vertex base. int vertexBase = vertexOffset; // Fetch current index. int currentIndex = CurrentEmitterData.FirstInFrontOrder ? Tail : Head; // no need to dereference these more than once Vector2 pivot = _emitterData.ParticlePivotPoint; T2DKeyGraph redLife = _emitterData.RedChannelLife; T2DKeyGraph greenLife = _emitterData.GreenChannelLife; T2DKeyGraph blueLife = _emitterData.BlueChannelLife; T2DKeyGraph visibilityLife = _emitterData.VisibilityLife; T2DKeyGraph visibilityScale = _parentEffect.CurrentEffectData.VisibilityScale; // Iterate all allocated particles. while (currentIndex != -1) { // Calculate Particle unit-age, so that we can get size float unitAge = particleManagerPool[currentIndex].Age / particleManagerPool[currentIndex].Lifetime; // Scale Size //Vector2 renderSize = 0.5f * particleManagerPool[currentIndex]._size; Vector2 renderSize; Vector2.Multiply(ref particleManagerPool[currentIndex]._size, 0.5f, out renderSize); renderSize.X *= _emitterData.SizeXLife[unitAge]; if (renderSize.X < 0.0f) renderSize.X = 0.0f; if (_emitterData.FixedParticleAspect) renderSize.Y = renderSize.X; else { renderSize.Y *= _emitterData.SizeYLife[unitAge]; if (renderSize.Y < 0.0f) renderSize.Y = 0.0f; } // Transform Particle, inline the math. Rotation2D rotation = new Rotation2D(MathHelper.ToRadians(particleManagerPool[currentIndex]._rotationAngle)); //Vector2 center = particleManagerPool[currentIndex]._position + renderSize * pivot - rotation.Rotate(renderSize * pivot); Vector2 center; Vector2.Multiply(ref renderSize, ref pivot, out center); Vector2 rotate = rotation.Rotate(center); Vector2.Subtract(ref center, ref rotate, out center); Vector2.Add(ref particleManagerPool[currentIndex]._position, ref center, out center); //Vector2 x = renderSize.X * rotation.X; //Vector2 y = renderSize.Y * rotation.Y; Vector2 x, y; Vector2 rotX = rotation.X, rotY = rotation.Y; float renX = renderSize.X, renY = renderSize.Y; Vector2.Multiply(ref rotX, renX, out x); Vector2.Multiply(ref rotY, renY, out y); // Get the color for this particle float redValue = redLife[unitAge]; float greenValue = greenLife[unitAge]; float blueValue = blueLife[unitAge]; float visibilityValue = visibilityLife[unitAge] * visibilityScale[unitAge]; Color color = new Color((byte)(redValue * 255.0f), (byte)(greenValue * 255.0f), (byte)(blueValue * 255.0f), (byte)(visibilityValue * 255.0f)); // Vertex #0. vertexScratch[vertexOffset++] = new GFXVertexFormat.PCTTBN(new Vector3(-x.X - y.X + center.X, -x.Y - y.Y + center.Y, 0.0f), color, textureCoords[0], new Vector2(0.0f, 0.0f), tangent, normal); // Vertex #1. vertexScratch[vertexOffset++] = new GFXVertexFormat.PCTTBN(new Vector3(x.X - y.X + center.X, x.Y - y.Y + center.Y, 0.0f), color, textureCoords[1], new Vector2(0.0f, 0.0f), tangent, normal); // Vertex #2. vertexScratch[vertexOffset++] = new GFXVertexFormat.PCTTBN(new Vector3(x.X + y.X + center.X, x.Y + y.Y + center.Y, 0.0f), color, textureCoords[2], new Vector2(0.0f, 0.0f), tangent, normal); // Vertex #3. vertexScratch[vertexOffset++] = new GFXVertexFormat.PCTTBN(new Vector3(y.X - x.X + center.X, y.Y - x.Y + center.Y, 0.0f), color, textureCoords[3], new Vector2(0.0f, 0.0f), tangent, normal); // Set to next index. currentIndex = CurrentEmitterData.FirstInFrontOrder ? particleManagerPool[currentIndex].Previous : particleManagerPool[currentIndex].Next; } }
static bool _FindContactPoints( Vector2[] srcPoly, Vector2 srcPosition, Vector2 srcVelocity, ref Rotation2D srcRotation, Vector2[] dstPoly, Vector2 dstPosition, Vector2 dstVelocity, ref Rotation2D dstRotation, Vector2 collisionNormal, float collisionTime, Vector2[] srcContacts, Vector2[] dstContacts, out int contactCount) { // Reset Contact Count. contactCount = 0; // Find Source Support Points. int srcSupportCount = _FindSupportPoints(srcPoly, srcPosition, srcVelocity, ref srcRotation, collisionNormal, collisionTime, _srcSupportPoints); // No contacts without support-points! if (srcSupportCount == 0) return false; // Find Destination Support Points. int dstSupportCount = _FindSupportPoints(dstPoly, dstPosition, dstVelocity, ref dstRotation, -collisionNormal, collisionTime, _dstSupportPoints); // No contacts without support-points! if (dstSupportCount == 0) return false; // Trivial Contact Check. if (srcSupportCount == 1 && dstSupportCount == 1) { // Simple Contact. srcContacts[contactCount] = _srcSupportPoints[0]; dstContacts[contactCount] = _dstSupportPoints[0]; // Increase Contact Count. contactCount++; // Return Conversion. return true; } // Calculate Perpendicular Normal. Vector2 perpNormal = new Vector2(-collisionNormal.Y, collisionNormal.X); // Calculate Source/Destination Points. float srcMin = Vector2.Dot(_srcSupportPoints[0], perpNormal); float srcMax = srcMin; float dstMin = Vector2.Dot(_dstSupportPoints[0], perpNormal); float dstMax = dstMin; // Check for Two support-points for source. if (srcSupportCount == 2) { // Set Max. srcMax = Vector2.Dot(_srcSupportPoints[1], perpNormal); // Reoder (if needed). if (srcMax < srcMin) { // Swap. TorqueUtil.Swap(ref srcMin, ref srcMax); // Swap Support Points. TorqueUtil.Swap(ref _srcSupportPoints[0], ref _srcSupportPoints[1]); } } // Check for Two support-points for destination. if (dstSupportCount == 2) { // Set Max. dstMax = Vector2.Dot(_dstSupportPoints[1], perpNormal); // Reoder (if needed). if (dstMax < dstMin) { // Swap. TorqueUtil.Swap(ref dstMin, ref dstMax); // Swap Support Points. TorqueUtil.Swap(ref _dstSupportPoints[0], ref _dstSupportPoints[1]); } } // Contacts? if (srcMin > dstMax || dstMin > srcMax) { // if collision time is negative then we are fully overlapped, in which // case we don't do this test (this test is whether or not two oncoming edges // will pass through each other). if (collisionTime >= 0.0f) { // Nope! return false; } } // Projected Segment. Vector2 projSeg; if (srcMin > dstMin) { // Project Src->Dst. _ProjectPointToSegment(_srcSupportPoints[0], _dstSupportPoints[0], _dstSupportPoints[1], out projSeg); // Note Contacts. srcContacts[contactCount] = _srcSupportPoints[0]; dstContacts[contactCount] = projSeg; // Increase Contact Count. contactCount++; } else { // Project Dst->Src. _ProjectPointToSegment(_dstSupportPoints[0], _srcSupportPoints[0], _srcSupportPoints[1], out projSeg); // Note Contacts. srcContacts[contactCount] = projSeg; dstContacts[contactCount] = _dstSupportPoints[0]; // Increase Contact Count. contactCount++; } // Other Variants. if (srcMin != srcMax && dstMin != dstMax) { if (srcMax < dstMax) { // Project. _ProjectPointToSegment(_srcSupportPoints[1], _dstSupportPoints[0], _dstSupportPoints[1], out projSeg); // Note Contacts. srcContacts[contactCount] = _srcSupportPoints[1]; dstContacts[contactCount] = projSeg; // Increase Contact Count. contactCount++; } else { // Project. _ProjectPointToSegment(_dstSupportPoints[1], _srcSupportPoints[0], _srcSupportPoints[1], out projSeg); // Note Contacts. srcContacts[contactCount] = projSeg; dstContacts[contactCount] = _dstSupportPoints[1]; // Increase Contact Count. contactCount++; } } // Return Contacts. return true; }
private Rectangle _GetNearbyTiles(float x, float y, float width, float height, float pad) { // use offset from tile map center. x -= _position.X; y -= _position.Y; if (Rotation != 0.0f) { // need to transform world-space box to tile map aligned box which includes // original box. Rotation2D rotation = new Rotation2D(MathHelper.ToRadians(Rotation)); // figure tile space position Vector2 newPos = new Vector2(x, y); newPos = rotation.Unrotate(newPos); x = newPos.X; y = newPos.Y; // compute tile space width/height, expanded to include all // of original box in tile aligned box Vector2 xvec = rotation.X; Vector2 yvec = rotation.Y; float newWidth = width * Math.Abs(xvec.X) + height * Math.Abs(xvec.Y); float newHeight = width * Math.Abs(yvec.X) + height * Math.Abs(yvec.Y); width = newWidth; height = newHeight; } float minX, minY, maxX, maxY; minX = ((x - (width * 0.5f)) / _tileSize.X) + (_mapSize.X * 0.5f); maxX = ((x + (width * 0.5f)) / _tileSize.X) + (_mapSize.X * 0.5f); minY = ((y - (height * 0.5f)) / _tileSize.Y) + (_mapSize.Y * 0.5f); maxY = ((y + (height * 0.5f)) / _tileSize.Y) + (_mapSize.Y * 0.5f); minX -= pad; minY -= pad; maxX += pad; maxY += pad; // make sure that returned rectangle only contains valid tile boundaries minX = MathHelper.Clamp(minX, 0.0f, _mapSize.X - 1); maxX = MathHelper.Clamp(maxX + 1, 0.0f, _mapSize.X - 1); minY = MathHelper.Clamp(minY, 0.0f, _mapSize.Y - 1); maxY = MathHelper.Clamp(maxY + 1, 0.0f, _mapSize.Y - 1); return new Rectangle((int)minX, (int)minY, (int)(maxX), (int)(maxY)); }
public override void Render(SceneRenderState srs) { Assert.Fatal(_renderedTileTypes.Count == 0, "previous render tiles not cleared"); #if DEBUG Profiler.Instance.StartBlock("T2DTileLayer.Render"); #endif if (_vb.IsNull) _CreateAndFillVB(); T2DSceneCamera cam = _sceneGraph.Camera as T2DSceneCamera; Vector2 camExtent = (cam.SceneMax - cam.SceneMin); // we want world space extents Vector2 camCenter = cam.CenterPosition; Rectangle r = _GetNearbyTiles(camCenter.X, camCenter.Y, camExtent.X, camExtent.Y, 0.1f); // Get rotation and tile radius for q&d check to see if tile // is on screen (for rotated case). float tileRadius = 0.51f * _tileSize.Length(); Rotation2D mapRotation = new Rotation2D(MathHelper.ToRadians(Rotation)); Matrix mapToWorld = Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)) * Matrix.CreateTranslation(Position.X, Position.Y, 0.0f); // track how many tiles we'll actually render int tileCount = 0; // collect all the tiles we're actually going to render and put them in the right batch bool doCheckRotation = _rotation != 0.0f; for (int j = r.Top; j <= r.Height; j++) { for (int i = r.Left; i <= r.Width; i++) { T2DTileObject tile = GetTileByGridCoords(i, j); if (tile == null) continue; // check for off-screen tiles (rotated case, but doesn't cull tiles off screen due to camera rotation) if (doCheckRotation) { Vector2 tilePos = tile.GetTileLocalPosition(_mapSize, _tileSize); Vector2 tileWorldPos = mapRotation.Rotate(tilePos) + Position; if (Math.Abs(tileWorldPos.X - camCenter.X) - tileRadius > 0.5f * camExtent.X || Math.Abs(tileWorldPos.Y - camCenter.Y) - tileRadius > 0.5f * camExtent.Y) continue; } // at this point we're committed to rendering this tile // for now simply add to the list of tiles for that // tile type so we can batch them up later tileCount++; int tileIdx = i + j * (int)_mapSize.X; if (tile.TileType._renderTiles.Count == 0) _renderedTileTypes.Add(tile.TileType); tile.TileType._renderTiles.Add(tileIdx); } } // Assuming we found some tiles, render all the batches. // Note: can't set 0 length index buffer without crashing, so // need to skip out of that case. if (tileCount != 0) { // We now know how many tiles we're going to render. Is our index buffer big enough? if (_indexBuffer.IsNull) { // index buffer cleared out...start over with size _renderIndexCount = 0; } if (tileCount * 6 > _renderIndexCount) { if (!_indexBuffer.IsNull) { _indexBuffer.Instance.Dispose(); _indexBuffer.Invalidate(); } _renderIndexCount = tileCount * 8; // go a little higher than current max _indexBuffer = ResourceManager.Instance.CreateDynamicIndexBuffer(ResourceProfiles.ManualDynamicIBProfile, _renderIndexCount * sizeof(UInt16), IndexElementSize.SixteenBits); } UInt16[] indexScratch = TorqueUtil.GetScratchArray<UInt16>(tileCount * 6); // render tile types in batches int idx = 0; srs.World.Push(); srs.World.MultiplyMatrixLocal(mapToWorld); Matrix worldTransform = srs.World.Top; VertexDeclaration vd = GFXVertexFormat.GetVertexDeclaration(srs.Gfx.Device); for (int i = 0; i < _renderedTileTypes.Count; i++) { T2DTileType tileType = _renderedTileTypes[i]; RenderInstance ri = SceneRenderer.RenderManager.AllocateInstance(); ri.Type = RenderInstance.RenderInstanceType.Mesh2D; ri.ObjectTransform = worldTransform; ri.PrimitiveType = PrimitiveType.TriangleList; ri.VertexDeclaration = vd; ri.VertexBuffer = _vb.Instance; ri.VertexSize = GFXVertexFormat.VertexSize; ri.BaseVertex = 0; ri.VertexCount = 4 * (int)_mapSize.X * (int)_mapSize.Y; ri.IndexBuffer = _indexBuffer.Instance; ri.StartIndex = idx; ri.PrimitiveCount = tileType._renderTiles.Count * 2; ri.Opacity = VisibilityLevel; ri.UTextureAddressMode = TextureAddressMode.Clamp; ri.VTextureAddressMode = TextureAddressMode.Clamp; ri.Material = tileType.Material; // fill up index buffer for (int j = 0; j < tileType._renderTiles.Count; j++) { int tileIdx = tileType._renderTiles[j]; Assert.Fatal(tileIdx * 4 + 3 < UInt16.MaxValue, "T2DTileLayer.Render - Got out of range tile index!"); indexScratch[idx++] = (UInt16)(4 * tileIdx + 0); indexScratch[idx++] = (UInt16)(4 * tileIdx + 1); indexScratch[idx++] = (UInt16)(4 * tileIdx + 2); indexScratch[idx++] = (UInt16)(4 * tileIdx + 0); indexScratch[idx++] = (UInt16)(4 * tileIdx + 2); indexScratch[idx++] = (UInt16)(4 * tileIdx + 3); } tileType._renderTiles.Clear(); SceneRenderer.RenderManager.AddInstance(ri); } srs.World.Pop(); // rendered all the batches _renderedTileTypes.Clear(); // set index data GFXDevice.Instance.Device.Indices = null; #if XBOX _indexBuffer.Instance.SetData<UInt16>(indexScratch, 0, idx); // XBox cannot discard... performance issue? #else _indexBuffer.Instance.SetData<UInt16>(indexScratch, 0, idx, SetDataOptions.Discard); #endif } // Renders custom collision polys for tiles based on tile types if (RenderCollisionBounds) { Matrix scaleMat = Matrix.CreateScale(_tileSize.X * 0.5f, _tileSize.Y * 0.5f, 1.0f); for (int j = r.Top; j <= r.Height; j++) { for (int i = r.Left; i <= r.Width; i++) { T2DTileObject tile = GetTileByGridCoords(i, j); if (tile == null || tile.TileType.CollisionPolyBasis == null) continue; // this is debug code so don't worry about all the math Vector2 tilePos = tile.GetTileLocalPosition(_mapSize, _tileSize); Matrix transMat = Matrix.CreateTranslation(tilePos.X, tilePos.Y, 0.0f); Matrix tileToWorld = scaleMat * transMat * mapToWorld; _RenderTileBounds(srs, ref tileToWorld, tile); } } } #if DEBUG Profiler.Instance.EndBlock("T2DTileLayer.Render"); #endif }
/// <summary> /// Find world position of tile assuming it belongs to tile layer with the specified parameters. /// </summary> /// <param name="mapPos">Position of the tile layer in world coordinates.</param> /// <param name="mapRotation">Rotation of the tile layer in degrees.</param> /// <param name="mapSize">Dimensions of the tile layer.</param> /// <param name="tileSize">Size of each tile in the tile layer.</param> /// <returns>World position of tile.</returns> public Vector2 GetWorldPosition(Vector2 mapPos, float mapRotation, Vector2 mapSize, Vector2 tileSize) { Vector2 local = GetTileLocalPosition(mapSize, tileSize); if (mapRotation != 0.0f) { Rotation2D rotate = new Rotation2D(MathHelper.ToRadians(mapRotation)); return rotate.Rotate(local) + mapPos; } else return local + mapPos; }
public static bool IntersectMovingPolyPoly( float elapsedTime, Vector2[] srcVertexList, Vector2[] dstVertexList, Vector2 srcPosition, Vector2 dstPosition, float srcRotation, float dstRotation, Vector2 srcVelocity, Vector2 dstVelocity, ref Vector2 collisionPosition, ref Vector2 collisionNormal, ref Vector2 collisionPenetration, ref float time) { if (srcVertexList == null || dstVertexList == null) return false; int srcVertexCount = srcVertexList.Length; int dstVertexCount = dstVertexList.Length; //src Rotation2D srcRot = new Rotation2D(MathHelper.ToRadians(srcRotation)); //dst Rotation2D dstInverseRot = new Rotation2D(MathHelper.ToRadians(-dstRotation)); Rotation2D refLocalRot = srcRot * dstInverseRot; Vector2 offset = srcPosition - dstPosition; Vector2 refLocalOffset = dstInverseRot.Rotate(offset); Vector2 velocity = srcVelocity - dstVelocity; Vector2 refLocalVelocity = dstInverseRot.Rotate(velocity); float refVelSqr = Vector2.Dot(refLocalVelocity, refLocalVelocity); int axesCount = 0; float fullTimeStep = elapsedTime; float Epsilon = 0.0001f; // Ignore small velocities! if (refVelSqr > Epsilon) { // Set Axis. _vertexAxis[axesCount] = new Vector2(-refLocalVelocity.Y, refLocalVelocity.X); // Check Interval Intersection. if (!_CheckIntervalIntersection( srcVertexList, dstVertexList, ref _vertexAxis[axesCount], ref refLocalOffset, ref refLocalVelocity, ref refLocalRot, ref _timeAxis[axesCount], fullTimeStep)) // No Collision! return false; // Next Axes. axesCount++; } // Test Seperation Axes for Source Object. // NOTE:- We ignore if it's a point! if (srcVertexCount > 1) { for (int j = srcVertexCount - 1, i = 0; i < srcVertexCount; j = i, i++) { // fetch the edge Vector2 dP = srcVertexList[i] - srcVertexList[j]; Vector2 dP2 = new Vector2(-dP.Y, dP.X); _vertexAxis[axesCount] = refLocalRot.Rotate(dP2); // check interval intersection if (!_CheckIntervalIntersection(srcVertexList, dstVertexList, ref _vertexAxis[axesCount], ref refLocalOffset, ref refLocalVelocity, ref refLocalRot, ref _timeAxis[axesCount], fullTimeStep)) return false; //next axes axesCount++; } } // Test Seperation Axes for Destination Object. // NOTE:- We ignore if it's a point! if (dstVertexCount > 1) { for (int j = dstVertexCount - 1, i = 0; i < dstVertexCount; j = i, i++) { // fetch the edge Vector2 dP = dstVertexList[i] - dstVertexList[j]; // set the axis _vertexAxis[axesCount] = new Vector2(-dP.Y, dP.X); if (!_CheckIntervalIntersection(srcVertexList, dstVertexList, ref _vertexAxis[axesCount], ref refLocalOffset, ref refLocalVelocity, ref refLocalRot, ref _timeAxis[axesCount], fullTimeStep)) return false; //next axes axesCount++; } } // Test Special-Case for Segments for Destination Object. if (dstVertexCount == 2) { // Set Axis. _vertexAxis[axesCount] = (dstVertexList[1] - dstVertexList[0]); // Check Interval Intersection. if (!_CheckIntervalIntersection(srcVertexList, dstVertexList, ref _vertexAxis[axesCount], ref refLocalOffset, ref refLocalVelocity, ref refLocalRot, ref _timeAxis[axesCount], fullTimeStep)) // No Collision! return false; // Next Axes. axesCount++; } // Test Special-Case for Segments for Source Object. if (srcVertexCount == 2) { // Set Axis. Vector2 segment = (srcVertexList[1] - srcVertexList[0]); _vertexAxis[axesCount] = refLocalRot.Rotate(segment); // Check Interval Intersection. if (!_CheckIntervalIntersection(srcVertexList, dstVertexList, ref _vertexAxis[axesCount], ref refLocalOffset, ref refLocalVelocity, ref refLocalRot, ref _timeAxis[axesCount], fullTimeStep)) // No Collision! return false; // Next Axes. axesCount++; } // find minimum seperation distance if (!_FindCollisionTime(_vertexAxis, _timeAxis, axesCount, ref collisionNormal, ref time)) return false; // Respace Normal. collisionNormal = dstInverseRot.Unrotate(collisionNormal); // need this below (simple transpose) Rotation2D dstRot = dstInverseRot.Invert(); // compute offset between poly centers for following calculation Vector2 polyCenter = new Vector2(); foreach (Vector2 v in srcVertexList) polyCenter += v; polyCenter *= 1.0f / (float)srcVertexList.Length; Vector2 polyOffset = srcPosition + srcRot.Rotate(polyCenter); polyCenter = new Vector2(); foreach (Vector2 v in dstVertexList) polyCenter += v; polyCenter *= 1.0f / (float)dstVertexList.Length; polyOffset -= dstPosition + dstRot.Rotate(polyCenter); // Make sure the collision polygons are pushed away. // NOTE: This is the overlap case. if (Vector2.Dot(collisionNormal, polyOffset) < 0.0f) collisionNormal = -collisionNormal; int contactCount = 0; if (!_FindContactPoints( srcVertexList, srcPosition, srcVelocity, ref srcRot, dstVertexList, dstPosition, dstVelocity, ref dstRot, collisionNormal, time, _srcContacts, _dstContacts, out contactCount)) // No Support Points! return false; collisionPosition = 0.5f * (_srcContacts[0] + _dstContacts[0]); collisionPenetration = _dstContacts[0] - _srcContacts[0]; if (contactCount == 2) { collisionPosition = 0.5f * collisionPosition + 0.25f * (_srcContacts[1] + _dstContacts[1]); Vector2 collisionPenetration2 = _dstContacts[1] - _srcContacts[1]; if (collisionPenetration2.LengthSquared() > collisionPenetration.LengthSquared()) collisionPenetration = collisionPenetration2; } // return true to indicate collision return true; }
//----------------------------------------------------------------------------------------------- // Transform Contact-Point into World-Space Collision. //----------------------------------------------------------------------------------------------- static void _TransformContact(Vector2 vertex, Vector2 position, Vector2 velocity, ref Rotation2D rotation, float collisionTime, out Vector2 contact) { // Do Transformation. contact = position + rotation.Rotate(vertex); // Check Time. if (collisionTime > 0.0f) contact += velocity * collisionTime; }
//----------------------------------------------------------------------------------------------- // Find Support Points. // NOTE:- This is a convex shape along a specified direction. //----------------------------------------------------------------------------------------------- static int _FindSupportPoints( Vector2[] poly, Vector2 position, Vector2 velocity, ref Rotation2D rotation, Vector2 collisionNormal, float collisionTime, Vector2[] supportPoints) { // Calculate Normal. Vector2 normal = rotation.Unrotate(collisionNormal); // Reset Direction. float supportMin = _support[0] = Vector2.Dot(poly[0], normal); // Interate Polygon. for (int i = 1; i < poly.Length; i++) { // Calculate. _support[i] = Vector2.Dot(poly[i], normal); // Check For Minimum. if (_support[i] < supportMin) supportMin = _support[i]; } // The limit here is two support-points only. // If we find more then we use the extremums. int supportCount = 0; // Set Threshold. float threshold = 1.0e-3f; // Reset Sign Flag. bool sign = false; // Calculate Perpendicular Normal. Vector2 perpNormal = new Vector2(-collisionNormal.Y, collisionNormal.X); // Interate Polygon. for (int i = 0; i < poly.Length; i++) { // Check Contact. if (_support[i] < supportMin + threshold) { // Transform Contact to World-Space. Vector2 contact; _TransformContact(poly[i], position, velocity, ref rotation, collisionTime, out contact); // Contact Dot. float contactDot = Vector2.Dot(contact, perpNormal); // Less than two supports? if (supportCount < 2) { // Yes, so note contact. _supportMinMax[supportCount] = contactDot; supportPoints[supportCount] = contact; // Increase Support Count. supportCount++; // Note Sign for two contacts. if (supportCount > 1) sign = (_supportMinMax[1] > _supportMinMax[0]); } else { int idx0 = 0; int idx1 = 1; if (!sign) TorqueUtil.Swap(ref idx0, ref idx1); if (contactDot < _supportMinMax[idx0]) { _supportMinMax[idx0] = contactDot; supportPoints[idx0] = contact; } else if (contactDot > _supportMinMax[idx1]) { _supportMinMax[idx1] = contactDot; supportPoints[idx1] = contact; } } } } // Return Support Count. return supportCount; }