Adam's Lair Forum

game development and casual madness
It is currently 2017/03/26, 11:18

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 2 posts ] 
Author Message
PostPosted: 2017/01/11, 11:24 
Junior Member
Junior Member

Joined: 2016/05/15, 18:27
Posts: 45
Role: Hobbyist
Hey,

So as you guys may now, I've been working on a 2d platformer shooter with various weapons. One weapon I'm having trouble implementing is the laser weapon. I want it to be fired like this:


So far, I've come up with firing a raycast and then stretching the prefab to that distance (since the prefab will be a straight line anyways). I don't know how you specifically use raycasts though (I don't know what methods to use), nor do I know how I would access this data.


Here's how I'm handling my other weapons(the FireWeapon() method is most relevant) (Yeah, it's pretty hacky, but I'm relatively new to programming):
Code:
using System.Collections.Generic;
using Duality;
using Duality.Components;
using Duality.Components.Physics;
using Duality.Components.Renderers;
using Duality.Input;
using Duality.Resources;
using Manager;
using Misc;
using Player;

namespace Behavior
{
    public enum WeaponAnimationState
    {
        Idle,
        PlayerMoving, //For when player is moving. Usually the same sprite as idle, except for things like the pepperthrower.
        Fire
    }

    /// <summary>
    ///     Gives the weapon object its basic behaviors, such as being attached to the player, and reflecting the sprite of the
    ///     currently held item.
    /// </summary>
    [RequiredComponent(typeof(AnimSpriteRenderer))]
    [RequiredComponent(typeof(Transform))]
    public class HeldWeapon : Component, ICmpUpdatable, ICmpInitializable
    {
        public Weapon CurrentWeapon { get; private set; }
        public Vector2 weaponOffset { get; set; } //Offset of weapon, relative to the center of the player.
        public Vector3 prefabSpawnOffset { get; set; } //Offset of where bullet is spawned relative to gun

        private ContentRef<Prefab> prefabToBeFired;
        private ContentRef<Prefab> peaPrefab;
        private ContentRef<Prefab> lemonadePrefab;
        private ContentRef<Prefab> pepperFlamePrefab;
        private ContentRef<Prefab> hydroLaserPrefab;
        private float firingDelay;
        private float firingDelayCounter;
        private float idleAnimationDelayCounter;
        private GameObject player;
        private Transform transform; //Transform of weapon, to be set to a position relative to player.
        private List<Weapon> weaponDatabase;
        private GameObject weaponDatabaseObject;
        private AnimSpriteRenderer heldWeaponAnimRenderer;
        private AnimSpriteRenderer playerAnimRenderer;
        private AnimationManager weaponAnimationManager; //Handles all animations related to weapons (Such as firing animations)
        private PlayerController playerController;
        private WeaponAnimationState animationState;

        void ICmpInitializable.OnInit(InitContext context)
        {
            if (context == InitContext.Activate)
            {
                //Getting references to components and gameobjects
                transform = GameObj.GetComponent<Transform>();
                player = GameObj.ParentScene.FindGameObject<PlayerController>();
                weaponAnimationManager = GameObj.GetComponent<AnimationManager>();
                weaponDatabaseObject = GameObj.ParentScene.FindGameObject<WeaponDatabaseManager>();
                weaponDatabase = weaponDatabaseObject.GetComponent<WeaponDatabaseManager>().weaponDatabase;
                heldWeaponAnimRenderer = GameObj.ParentScene.FindGameObject<HeldWeapon>().GetComponent<AnimSpriteRenderer>();
                playerAnimRenderer = player.GetComponent<AnimSpriteRenderer>();
                playerController = player.GetComponent<PlayerController>();

                CurrentWeapon = null;
                transform.Pos = new Vector3(0, 0, -0.1f);
                peaPrefab = new ContentRef<Prefab>(null, @"Data\Prefabs\Weapons\Pea.Prefab.res");
                lemonadePrefab = new ContentRef<Prefab>(null, @"Data\Prefabs\Weapons\Lemonade.Prefab.res");
                pepperFlamePrefab = new ContentRef<Prefab>(null, @"Data\Prefabs\Weapons\FlamethrowerParticle.Prefab.res");
                firingDelayCounter = 0f;
                heldWeaponAnimRenderer.SharedMaterial.Res.MainTexture = null;
                ChangeHeldWeapon(2);
                animationState = WeaponAnimationState.Idle;
            }
        }

        void ICmpInitializable.OnShutdown(ShutdownContext context)
        {
        }

        void ICmpUpdatable.OnUpdate()
        {
            firingDelayCounter += Time.MsPFMult * Time.TimeMult;
            idleAnimationDelayCounter += Time.MsPFMult * Time.TimeMult;
            MoveWeaponWithPlayer();
            UpdateAnimation();
            SwitchWeapon();

            if (CurrentWeapon.TypeOfProjectile == ProjectileType.Pea ||
                CurrentWeapon.TypeOfProjectile == ProjectileType.Lemonade)
            {
                if (DualityApp.Keyboard.KeyHit(Key.X) && (firingDelayCounter >= firingDelay))
                {
                    firingDelayCounter = 0;
                    heldWeaponAnimRenderer.AnimTime = 0; //Reset animation to beginning (Don't know if this actually does anything).
                    weaponAnimationManager.PlayAnimationOnce("fire", "idle");
                    animationState = WeaponAnimationState.Fire;
                    FireWeapon();
                }
                else if (DualityApp.Keyboard.KeyPressed(Key.Left) || DualityApp.Keyboard.KeyPressed(Key.Right))
                    animationState = WeaponAnimationState.PlayerMoving;
            }
            else if (CurrentWeapon.TypeOfProjectile == ProjectileType.Pepper)
            {
                if (DualityApp.Keyboard.KeyPressed(Key.Left) || DualityApp.Keyboard.KeyPressed(Key.Right)) //Player is moving.
                {
                    if (DualityApp.Keyboard.KeyPressed(Key.X)) //Player is trying to attack.
                    {
                        animationState = WeaponAnimationState.Fire; //Weapon is in attack animation.

                        if (firingDelayCounter >= firingDelay) //Firing is allowed by timer.
                        {
                            firingDelayCounter = 0;
                            FireWeapon();
                        }
                    }
                    else //Player is not trying to attack.
                    {
                        animationState = WeaponAnimationState.PlayerMoving; //Weapon is in Moving animation.
                    }
                }
                else if (DualityApp.Keyboard.KeyPressed(Key.X)) //Player is not moving, but is trying to attack.
                {
                    animationState = WeaponAnimationState.Fire; //Weapon is in attack animation.

                    if (firingDelayCounter >= firingDelay) //Firing is allowed by timer.
                    {
                        firingDelayCounter = 0;
                        FireWeapon();
                    }
                }
                else if ((DualityApp.Keyboard.KeyPressed(Key.Left) == false && //Neither movement keys are being used, and fire key is not pressed
                          DualityApp.Keyboard.KeyPressed(Key.Right) == false)
                         && DualityApp.Keyboard.KeyPressed(Key.X) == false)
                {
                    animationState = WeaponAnimationState.Idle; //Player is giving no input, making weapon idle
                }
            }
        }

        /// <summary>
        /// Loops through database to change weapon to the weapon that has the inputted ID.
        /// </summary>
        /// <param name="weaponToChangeToID"></param>
        public void ChangeHeldWeapon(int weaponToChangeToID)
        {
            foreach (Weapon _weapon in weaponDatabase)
            {
                if (_weapon.ID == weaponToChangeToID)
                {
                    CurrentWeapon = _weapon;
                    UpdateHeldWeaponComponents();
                    break;
                }
                else
                    continue;
            }
        }

        /// <summary>
        /// Changes relevant components so the currentWeapon's data is swapped in.
        /// </summary>
        private void UpdateHeldWeaponComponents()
        {
            heldWeaponAnimRenderer.SharedMaterial.Res.MainTexture = CurrentWeapon.Sprite; //Update weapon sprite
            firingDelay = CurrentWeapon.RateOfFire * 100f; //Update firing rate
            weaponOffset = new Vector2(CurrentWeapon.XOffset, CurrentWeapon.YOffset); //Load in offset from .json file.
            prefabSpawnOffset = new Vector3(CurrentWeapon.PrefabXOffset, CurrentWeapon.PrefabYOffset, 0.3f); //Load in offset of where prefab is spawned
            weaponAnimationManager.animatedObjectName = CurrentWeapon.Slug; //Change animation path to new weapon's name.
            weaponAnimationManager.ConstructAnimationDatabase(); //Reconstruct animation data from new .json.

            //Update dimensions of rect
            heldWeaponAnimRenderer.Rect = new Rect(CurrentWeapon.TopLeft, CurrentWeapon.TopRight,
                                                   CurrentWeapon.BottomLeft, CurrentWeapon.BottomRight);

            //Update prefab
            switch (CurrentWeapon.TypeOfProjectile)
            {
                case ProjectileType.Pea:
                    prefabToBeFired = peaPrefab;
                    break;

                case ProjectileType.Lemonade:
                    prefabToBeFired = lemonadePrefab;
                    break;

                case ProjectileType.Pepper:
                    prefabToBeFired = pepperFlamePrefab;
                    break;

                case ProjectileType.Hydro:
                    break;
            }
        }

        /// <summary>
        /// Moves weapon with player at an offset, so it appears the player is holding the weapon.
        /// Also moves weapon with player when he is moving.
        /// </summary>
        private void MoveWeaponWithPlayer()
        {
            int _currentAnimationFrame = playerAnimRenderer.CurrentFrame + 1; //Compensate for zero-based indexing.
            Vector2 _animationOffset = new Vector2(0, 0); //Offset added to y-axis to compensate for movement in animations.

            if (playerController.PlayerAnimationState == PlayerAnimationState.Idle)
            {
                switch (_currentAnimationFrame)
                {
                    //First two frames have no vertical movement.
                    case 1:
                    case 2:
                        _animationOffset = new Vector2(0, 0);
                        break;
                   
                    //In third and sixth frames, moves down one pixel relative to original.
                    case 3:
                    case 6:
                        _animationOffset = new Vector2(0, 1);
                        break;

                    //In fourth and fifth frames, moves down two pixels relative to original.
                    case 4:
                    case 5:
                        _animationOffset = new Vector2(0, 2);
                        break;
                }
            }
            else if (playerController.PlayerAnimationState == PlayerAnimationState.Running)
            {
                switch (_currentAnimationFrame)
                {
                    //No vertical movement on these frames.
                    case 7:
                    case 9:
                        _animationOffset = new Vector2(0, 0);
                        break;
                   
                    //In eight and tenth frames, moves down one pixel relative to original.
                    case 8:
                    case 10:
                        _animationOffset = new Vector2(0, 1);
                        break;
                }
            }

            //Flip weapon and keep it at a consistent part of the player. Also used to move player with animations.
            switch (player.GetComponent<PlayerController>().FacingDirection)
            {
                case FacingDirection.Right:
                    //Places weapon sprite on player, and flips it according to facing direction.
                    GameObj.Transform.Pos = new Vector3(player.Transform.Pos.X + weaponOffset.X,
                                                        player.Transform.Pos.Y + weaponOffset.Y + _animationOffset.Y, 0.2f);
                    heldWeaponAnimRenderer.Flip = SpriteRenderer.FlipMode.None;
                    break;

                case FacingDirection.Left:
                    //Places weapon sprite on player, and flips it according to facing direction.
                    GameObj.Transform.Pos = new Vector3(player.Transform.Pos.X - weaponOffset.X,
                                                        player.Transform.Pos.Y + weaponOffset.Y + _animationOffset.Y, 0.2f);
                    heldWeaponAnimRenderer.Flip = SpriteRenderer.FlipMode.Horizontal;
                    break;
            }

           
        }

        /// <summary>
        /// Instantiates correct prefab for heldWeapon.
        /// </summary>
        private void FireWeapon()
        {
            for (var i = 0; i < CurrentWeapon.BurstCount; i++)
            {
                GameObject _firedPrefabInstance;
                Pea _peaScript;
                Lemonade _lemonScript;
                PepperFlameParticle _pepperScript;
                var _positiveNegativeOffset = MathF.Rnd.Next(0, 2); //Generate random value 0 or 1
                _positiveNegativeOffset = _positiveNegativeOffset == 0 ? -1 : 1; //is positiveNegativeOffset equal to zero? Set it to 1. Otherwise set it to -1
                var _bulletAngleOffset = MathF.DegToRad(MathF.Rnd.NextFloat(0, CurrentWeapon.Inaccuracy) * _positiveNegativeOffset); //Generates offset in radians

                var _flameParticleRotation = MathF.Rnd.Next(0, 361);

                //Checks which direction player is facing, adjusting prefab values accordingly.
                switch (player.GetComponent<PlayerController>().FacingDirection)
                {
                    case FacingDirection.Right:

                        if (prefabToBeFired == peaPrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X + prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _peaScript = _firedPrefabInstance.GetComponent<Pea>();
                            _peaScript.PeaSpriteRenderer.Flip = SpriteRenderer.FlipMode.None; //No need to flip sprite since it was made facing right.

                            //Set projectile's speed
                            _peaScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _peaScript.Speed,
                                                                         MathF.Sin(_bulletAngleOffset) * _peaScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<Pea>().Creator = player;
                        }
                        else if (prefabToBeFired == lemonadePrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X + prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _lemonScript = _firedPrefabInstance.GetComponent<Lemonade>();
                            _lemonScript.LemonadeSpriteRenderer.Flip = SpriteRenderer.FlipMode.None; //No need to flip sprite since it was made facing right.

                            //Set projectile's speed
                            _lemonScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _lemonScript.Speed,
                                                                           MathF.Sin(_bulletAngleOffset) * _lemonScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<Lemonade>().Creator = player;
                        }
                        else if (prefabToBeFired == pepperFlamePrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X + prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _pepperScript = _firedPrefabInstance.GetComponent<PepperFlameParticle>();
                            _pepperScript.PepperSpriteRenderer.Flip = SpriteRenderer.FlipMode.None; //No need to flip sprite since it was made facing right.

                            //Set projectile's speed
                            _pepperScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _pepperScript.Speed,
                                                                            MathF.Sin(_bulletAngleOffset) * _pepperScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<PepperFlameParticle>().Creator = player;
                        }
                        break;

                    case FacingDirection.Left:

                        if (prefabToBeFired == peaPrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X - prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _peaScript = _firedPrefabInstance.GetComponent<Pea>();
                            _peaScript.PeaSpriteRenderer.Flip = SpriteRenderer.FlipMode.Horizontal; //Flip sprite so it faces correct direction.

                            //Set projectile's speed
                            _peaScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _peaScript.Speed * -1,
                                                                         MathF.Sin(_bulletAngleOffset) * _peaScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<Pea>().Creator = player;
                        }
                        else if (prefabToBeFired == lemonadePrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X - prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _lemonScript = _firedPrefabInstance.GetComponent<Lemonade>();
                            _lemonScript.LemonadeSpriteRenderer.Flip = SpriteRenderer.FlipMode.Horizontal; //Flip sprite so it faces correct direction.

                            //Set projectile's speed
                            _lemonScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _lemonScript.Speed * -1,
                                                                           MathF.Sin(_bulletAngleOffset) * _lemonScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<Lemonade>().Creator = player;
                        }
                        else if (prefabToBeFired == pepperFlamePrefab)
                        {
                            //Spawn prefab at gun + an offset to compensate for gun opening's position.
                            _firedPrefabInstance = prefabToBeFired.Res.Instantiate(new Vector3(GameObj.Transform.Pos.X - prefabSpawnOffset.X,
                                                                                  GameObj.Transform.Pos.Y + prefabSpawnOffset.Y, -0.1f),
                                                                                  _bulletAngleOffset);

                            _pepperScript = _firedPrefabInstance.GetComponent<PepperFlameParticle>();
                            _pepperScript.PepperSpriteRenderer.Flip = SpriteRenderer.FlipMode.Horizontal; //Flip sprite so it faces correct direction.

                            //Set projectile's speed
                            _pepperScript.LinearVelocityToSet = new Vector2(MathF.Cos(_bulletAngleOffset) * _pepperScript.Speed * -1,
                                                                            MathF.Sin(_bulletAngleOffset) * _pepperScript.Speed);

                            Scene.Current.AddObject(_firedPrefabInstance);
                            _firedPrefabInstance.GetComponent<PepperFlameParticle>().Creator = player;
                        }
                        break;
                }
            }
        }

        public void UpdateAnimation()
        {
            switch (animationState)
            {
                case WeaponAnimationState.Idle:
                    weaponAnimationManager.PlayAnimationContinuously("idle");
                    break;

                case WeaponAnimationState.PlayerMoving:
                    weaponAnimationManager.PlayAnimationContinuously("playerMoving");
                    break;

                case WeaponAnimationState.Fire:
                    //Really hacky solution, change later.
                    if (CurrentWeapon.TypeOfProjectile == ProjectileType.Pepper)
                    {
                        weaponAnimationManager.PlayAnimationContinuously("fire");
                    }
                    break;
            }
        }

        /// <summary>
        /// Allows weapons to be cycled through using number keys. Not to be included in the final game.
        /// </summary>
        private void SwitchWeapon()
        {
            if (DualityApp.Keyboard.KeyHit(Key.Number1))
                ChangeHeldWeapon(0);
            else if (DualityApp.Keyboard.KeyHit(Key.Number2))
                ChangeHeldWeapon(1);
            else if (DualityApp.Keyboard.KeyHit(Key.Number3))
                ChangeHeldWeapon(2);
        }
    }
}



Could I use the already implemented logic to find an angle for a raycast, and fire it from the weapon?


Top
 Profile  
 
PostPosted: 2017/01/11, 14:16 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 1933
Location: Germany
Role: Professional
Yep, you're pretty much on the right track with pursuing raycasts for this. Here's a quick sample that should help get you started:

Code:
   [RequiredComponent(typeof(Transform))]
   public class RaycastSample : Component, ICmpUpdatable
   {
      void ICmpUpdatable.OnUpdate()
      {
         // Determine the world position of the mouse cursor
         Camera mainCam = this.GameObj.ParentScene.FindComponent<Camera>();
         Vector3 mousePosWorld = mainCam.GetSpaceCoord(DualityApp.Mouse.Pos);

         // If the mouse is exactly at this object's position, there's no
         // direction we could cast our ray towards.
         if (mousePosWorld.Xy == this.GameObj.Transform.Pos.Xy) return;

         // Start at this object's position
         Vector2 startPos = this.GameObj.Transform.Pos.Xy;
         // Find out the direction of the ray based on the mouse position relative to this object
         Vector2 direction = (mousePosWorld.Xy - startPos).Normalized;
         // Determine the ray's end position using direction and length
         float maxLength = 500.0f;
         Vector2 endPos = startPos + direction * maxLength;

         // Perform the raycast. The callback we provide will react to all kinds of RigidBodies.
         RayCastData firstHit;
         bool hitAnything = RigidBody.RayCast(startPos, endPos, hitData => hitData.Fraction, out firstHit);

         // If we hit anything, draw a line from start to hitpoint and display what we hit
         if (hitAnything)
         {
            VisualLog.Default.DrawConnection(startPos.X, startPos.Y, 0.0f, firstHit.Pos.X, firstHit.Pos.Y);
            VisualLog.Default.DrawText(firstHit.Pos.X, firstHit.Pos.Y, 0.0f, firstHit.GameObj.FullName);
         }
         // If we didn't hit anything, draw a line where our ray went through
         else
         {
            VisualLog.Default.DrawConnection(startPos.X, startPos.Y, 0.0f, endPos.X, endPos.Y);
         }
      }
   }


Just put that component somewhere in your game world, and run the game, it will do a raycast from its own position to the mouse position and display the result in a visual log. It's rays are limited to a maximum length of 500, so make sure there's something interesting to hit in that range and your camera / cursor is near enough.

You can use the raycast callback to ignore certain objects. Here's a more complex example from the space shooter sample package that checks if an enemy has line-of-sight of another object.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group