Adding bullets...?

Nov 20, 2012 at 11:35 AM

Hi all,

I'm trying to add 'bullets' to my game with a speed and direction, also a timer and such.

I made a Particle in XAML, a fire sphere thingie.
Now I want to 'shoot' it from the model I'm controlling.
 
I thought about using Scene.add, but is there a way to do it via XAML?


Stefan.

Coordinator
Nov 20, 2012 at 12:22 PM

XAML is ideal for static representations, you need to create a custom component for this type of interaction.

What I have in mind is to create a Shooter component with a bullet template property. When the shooter starts firing, it emits new bullets based on the template and the parent transform. Though the component is using Scene.Add method, the component itself can be added to the scene using XAML. The template can be represented using string and loaded using Instance class.

This native approach might not be fast enough for bullets. Consider pool the bullets since Add/Remove from the scene is much slower then making them invisible.

Nov 20, 2012 at 7:01 PM

I understand the shooter component and how to add it to the scene using XAML.
What I don't understand is the template which you are talking about.
Also how do I declare the shooting direction?

I've looked at the ExampleController, which is pretty easy to use, to control a model.
However is there also a direction the 'player' is facing? 

Thanks.

Developer
Nov 20, 2012 at 7:08 PM

Oh, sorry I have was working on some more Components that I was going to commit but haven't gotten around to publish them and there are some stuff I need to clean up. 

I used

var Scene = this.Parent.FindRoot() as Scene;
var Camera = Scene.GetDrawingContext(Scene.GetGraphicsDevice()).Camera as Nine.Graphics.Cameras.Camera;

to get the camera. 

Nov 21, 2012 at 2:38 PM

Alright I think I'm on the right path, but I'm having some trouble with 'var'and 'scene' in my Shooter class.

Here are my codes:

Shooter.cs:

 

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Nine;
using Nine.Components;
using Nine.Graphics;

namespace NineQ
{
    class Shooter : Component
    {
        public bool IsShooting { get; set; }
        int timeBetweenShots = 300;
        int shotTimer = 0;
        List<Bullet> bullets;

        Scene scene;
        var tank1 = scene.FindName<Transformable>("Char");

        public void Update(GameTime gameTime)
        {
            KeyboardState keyboard = Keyboard.GetState();
            if (keyboard.IsKeyDown(Keys.LeftControl))
            {
                this.IsShooting = true;
            }
            else
            {
                this.IsShooting = false;
            }


            if (IsShooting)
            {
                shotTimer += gameTime.ElapsedGameTime.Milliseconds;

                if (shotTimer > timeBetweenShots)
                {
                    shotTimer = 0;

                    //add a new bullet

                    Bullet b = new Bullet(
                         tank1.Position,
                         tank1.Direction,
                         12, // The Speed
                         2000); // The active time in Milliseconds

                    bullets.Add(b);
                }
            }

            for (int i = 0; i < bullets.Count; i++)
            {
                bullets[i].Update(gameTime);

                if (bullets[i].TotalActiveTime > bullets[i].ActiveTime)
                    bullets.RemoveAt(i);
            }
        }
    }
}

 

And this is the Bullet class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Nine;

namespace NineQ
{
    class Bullet
    {
        public Vector3 Position { get; set; }
        public Vector3 Direction { get; set; }
        public float Speed { get; set; }
        public int ActiveTime { get; set; }
        public int TotalActiveTime { get; set; }

        public Bullet(Vector3 position, Vector3 direction, float speed, int activeTime)
        {
            this.Position = position;
            this.Direction = direction;
            this.Speed = speed;
            this.ActiveTime = activeTime;

            this.TotalActiveTime = 0;
        }

        public void Update(GameTime gameTime)
        {
            this.Position += Direction * Speed;

            this.TotalActiveTime += gameTime.ElapsedGameTime.Milliseconds;
        }

        public void Draw()
        {
            // Use a partcle effect defined in a XAML scene
        }
    }
}

And here are my errors:
Error 16 A field initializer cannot reference the non-static field, method, or property 'NineQ.Shooter.scene' 
Error 15 The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?)

So how can I use 'var' and 'scene' in my other classes?
Also I want to shoot a particle effect it, which I made in XAML.

Thanks
Stefan 

  


 

Developer
Nov 21, 2012 at 2:50 PM

 

Transformable tank1 = scene.FindName<Transformable>("Char");
Scene scene = Parent.FindRoot() as Scene;

var can only be used in functions, from what I know.

Nov 21, 2012 at 11:46 PM

Thanks to the great help of ChFlashER, I've come to a code without errors.
However I'm still not shooting stuff.

Shooter.cs and Bullet.cs are now in a Library called NineQData, they're added as references so they work properly.

What am I doing wrong?
All my code is below... 

Shooter.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Nine;
using Nine.Components;
using Nine.Graphics;

namespace NineQData
{
    public class Shooter : Component
    {
        public bool IsShooting { get; set; }
        private Microsoft.Xna.Framework.Graphics.Model Model { get; set; }
        public string ModelPath { get; set; }

        int timeBetweenShots = 300;
        int shotTimer = 0;
        List bullets;

        public void Update(GameTime gameTime)
        {
            var scene = Parent.FindRoot();
            var tank1 = scene.FindName("Char");

            KeyboardState keyboard = Keyboard.GetState();
            if (keyboard.IsKeyDown(Keys.LeftControl))
            {
                this.IsShooting = true;
            }
            else
            {
                this.IsShooting = false;
            }


            if (IsShooting)
            {
                shotTimer += gameTime.ElapsedGameTime.Milliseconds;

                if (shotTimer > timeBetweenShots)
                {
                    shotTimer = 0;

                    //add a new bullet

                    Bullet b = new Bullet(
                         Model,
                         tank1.Transform.Translation,
                         tank1.Transform.Forward,
                         12, // The Speed
                         2000); // The active time in Milliseconds

                    bullets.Add(b);
                }
            }

            for (int i = 0; i < bullets.Count; i++)
            {
                bullets[i].Update(gameTime);

                if (bullets[i].TotalActiveTime > bullets[i].ActiveTime)
                    bullets.RemoveAt(i);
            }
        }
    }
}

 

Bullet.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Nine;
using Nine.Graphics;

namespace NineQData
{
    public class Bullet
    {
        public Microsoft.Xna.Framework.Graphics.Model BulletModel { get; set; }
        public Vector3 Position { get; set; }
        public Vector3 Direction { get; set; }
        public float Speed { get; set; }
        public int ActiveTime { get; set; }
        public int TotalActiveTime { get; set; }

        public Bullet(Microsoft.Xna.Framework.Graphics.Model bulletmodel, Vector3 position, Vector3 direction, float speed, int activeTime)
        {
            this.BulletModel = bulletmodel;
            this.Position = position;
            this.Direction = direction;
            this.Speed = speed;
            this.ActiveTime = activeTime;

            this.TotalActiveTime = 0;
        }

        public void Update(GameTime gameTime)
        {
            this.Position += Direction * Speed;

            this.TotalActiveTime += gameTime.ElapsedGameTime.Milliseconds;
        }

        public void Draw()
        {
            // Use a partcle effect defined in a XAML scene
        }
    }
}

 

TestScene.xaml

<Scene xmlns="http://schemas.microsoft.com/nine/2011/xaml"
       xmlns:my="clr-namespace:NineQData;assembly=NineQData">
    <!--<Surface Heightmap.Width="512" Heightmap.Height="512" Heightmap.Step="1" PatchSegmentCount="8"
             Transform.Position="-64, 0, -64" TextureTransform.Scale="0.2, 0.2" VertexType="VertexPositionNormalTangentBinormalTexture" />-->

    <!--<BirdEyeCamera />-->
    <SkyBox Texture="{ContentReference ../Assets/Glacier}" />
    <Fog FogColor="150, 1, 1" FogStart="1" FogEnd="10" />
    <AmbientLight AmbientLightColor="0.2, 0.2, 0.2" />
    <DirectionalLight DiffuseColor="0.8, 0.8, 0.8" Direction="-1, -1, -1" />

    <!--<Group Name="Gebouw">
        <Model Source="{ContentReference ../Building/Sanssouci}" Transform.Position="256, 18, 256" 
               Transform.Scale="0.05,0.05,0.05" Transform.Rotation="{Degrees 0,180,0}"/>
    </Group>-->

    <Group Name="Tank" Transform.Scale="0.05, 0.05, 0.05">
        <Model Name="Char" Source="{ContentReference ../Assets/Tank}" Transform.Rotation="{Degrees 0, 0,0}"/>
        <my:Shooter ModelPath="{ContentReference ../Sphere}"/>
    </Group>

    <!-- Particle effects can also be combined using Group -->
    <Group Transform.Position="40, 6, 0">

        <ParticleEffect Name="Fireball" Texture="{ContentReference ../fire}">
            <ParticleEffect.Emitter>
                <SphereEmitter Emission="500" Duration="2 ~ 3" Speed="-0.2 ~ 0.2" Size="0.1 ~ 0.3" Shell="True" Radius="3" />
            </ParticleEffect.Emitter>

            <FadeController />
        </ParticleEffect>

        <!-- 
            When particles are rendered as constrained billboard, they always travels along the moving
            direction. This make it simple to create effects such as rains.
        -->
        <ParticleEffect Name="Vuurspark" Texture="{ContentReference ../fire}" ParticleType="ConstrainedBillboard" Stretch="8">
            <ParticleEffect.Emitter>
                <PointEmitter Emission="10" Duration="2 ~ 3" Speed="-0.01 ~ 0.01" Size="1 ~ 1.5" />
            </ParticleEffect.Emitter>
            <FadeController />
        </ParticleEffect>

    </Group>

    <HighDynamicRangeEffect Name="Hdr" Exposure="0.5" BloomIntensity="0.25" />
</Scene>

Developer
Nov 21, 2012 at 11:51 PM

 

<my:Shooter ModelPath="{ContentReference ../Sphere}"/> // Coulden't load a Model in here,

So specify path to the model and load it in the 'OnAdded'

 

protected override void OnAdded(Nine.Group parent)
        {
            var Scene = parent.FindRoot<Scene>();
            var Content = Scene.GetContentManager();

            Model = Content.Load<Microsoft.Xna.Framework.Graphics.Model>(ModelPath);

            base.OnAdded(parent);
        }

But I wonder if that works

Nov 22, 2012 at 2:59 PM

It says that the ModelPath is 'null'.
So I guess in the XAML it's not setting the ModelPath correctly or something.

I'll try a diffrent way around, by not using the <my:Shooter> in XAML. 

Nov 22, 2012 at 5:12 PM
Edited Nov 22, 2012 at 9:59 PM

=EDIT=

I'm almost done with a very dynamic ShotManager and Bullet.
However I'm having trouble with 'drawing' a 3D model.

 

public void Draw(Model model)
        {
            DrawModel(BulletModel, world, view, projection);
        }

        private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;
                }

                mesh.Draw();
            }
        }

 

 

The thing is, I need to know what to fill in for 'world', 'view' and 'projection'.

Since Engine Nine uses scenes and already has camera's and stuff, I wondered if I could implement those in there.
But I have no idea what should go where.

Stefan. 

Coordinator
Nov 23, 2012 at 1:53 AM
Edited Nov 23, 2012 at 9:02 AM
Hi stefanorie, please wait for a few days for me to put up a shooting game sample :-D
Coordinator
Nov 23, 2012 at 11:13 AM

Take a look at this rough implementation

http://nine.codeplex.com/SourceControl/changeset/view/f3d6a4438950#Samples%2fTutorial%2fTutorialData%2fExampleShooter.cs

Coordinator
Nov 23, 2012 at 11:17 AM

And you don't need to manually draw a model, just add a Nine.Graphics.Model to the scene and you are good to go.

Nov 23, 2012 at 12:54 PM

Hi Yufeih,

Thanks for the example!
I will try it right away.

Can you also shortly explain how I can use 'if' in XAML?
Like:
if a player is walking, he must be animated.
if he is standing still, he must not be animated.

Thanks!

Nov 28, 2012 at 1:59 PM

I tried out the ExampleShooter, but it gave me alot of errors.
So I downloaded the source code and it got the exact same errors.

The thing is, it first says it can't find the 'Update' methode to Override.
So I removed 'Override' in the method and got a new error. 

Error 1 An object reference is required for the non-static field, method, or property 'Nine.Group.Add(object)'
ExampleShooter.cs 27 17 TutorialData

Error 2 An object reference is required for the non-static field, method, or property 'Nine.Group.Remove(object)'
ExampleShooter.cs 44 17 TutorialData

How can I fix this?
I really want to shoot stuff... 

Coordinator
Nov 29, 2012 at 1:52 AM

You are probably using an older version. Remove all the existing dll references and try this installer http://nine.codeplex.com/downloads/get/552444

Nov 29, 2012 at 5:03 PM
Edited Nov 29, 2012 at 6:08 PM
yufeih wrote:

You are probably using an older version. Remove all the existing dll references and try this installer http://nine.codeplex.com/downloads/get/552444

I've installed the 'new' Engine Nine and added the references.
But I'm still getting the error!
Even when I copy your ExampleController.

Error 2 'NineQData.ExampleController.Update(float)': no suitable method found to override.

So... How can I fix it? 


= EDIT =
Alright I changed (float elapsedTime) to (TimeSpan elapsedTime), however now I can't use elapsedTime in the transformation thing.
So now I still got the scene.Add error :(

Error 1 An object reference is required for the non-static field, method, or property 'Nine.Group.Add(object)'

 

Dec 3, 2012 at 1:53 PM
Edited Dec 3, 2012 at 1:58 PM

I got shooting working now, but the direction is not working properly.

 

namespace NineQData
{
    using System;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Input;
    using Nine;

    public class ExampleShooter : Component
    {
        private Input input;

        public string BulletTemplate { get; set; }
        public Keys Trigger { get; set; }

        protected override void OnAdded(Group parent)
        {
            input = new Input();
            input.KeyDown += new EventHandler<KeyboardEventArgs>(OnKeyDown);
        }

        private void OnKeyDown(object sender, KeyboardEventArgs e)
        {
            if (e.Key == Trigger)
            {
                var bullet = new Instance() { Template = BulletTemplate };
                bullet.Transform = Parent.Transform;
                Scene.Add(bullet);
            }
        }
    }

    public class ExampleBullet : Component
    {
        private float lifetime;
        private Vector3 direction;

        public float BulletSpeed { get; set; }
        public TimeSpan Duration { get; set; }

        public bool changeBulletDirection = true;

        protected override void Update(float elapsedTime)
        {
            lifetime += elapsedTime;
            if (lifetime > Duration.TotalSeconds)
            {
                Scene.Remove(Parent);
                changeBulletDirection = true;
                return;
            }

            var transform = Parent.Transform;
            var keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.A) && changeBulletDirection)
            {
                direction = transform.Right;
            }
            if (keyboardState.IsKeyDown(Keys.D) && changeBulletDirection)
            {
                direction = transform.Left;
            }
            if (keyboardState.IsKeyDown(Keys.S) && changeBulletDirection)
            {
                direction = transform.Forward;
            }
            if (keyboardState.IsKeyDown(Keys.W) && changeBulletDirection)
            {
                direction = transform.Backward;
            }

            transform.Translation += direction * BulletSpeed * elapsedTime;
            Parent.Transform = transform;
        }
    }
}

As you can see in the above code, the way the bullet is flying is determined by:transform.Translation += direction * BulletSpeed * elapsedTime;However if i move with my player, the bullet will move with it.It's because it keeps checking my keys.
I tried using a boolean to define when I could change the direction and when not.But that didn't turn out well.
How can I fix this?

Developer
Dec 3, 2012 at 3:55 PM

Should the bullet still be moving forward when you press any of WASD keys?

Dec 3, 2012 at 6:48 PM

Well I now have it working, but still in a diffrent way.

I want the bullet to do the following:
- Fly in the direction the player is facing, when the player is standing still.
- Fly in the direction the player is facing, when the player is walking. 

At this point, I can only shoot when I walk, because when I'm standing still I'm like 'pooping' fire bullets. 

Developer
Dec 3, 2012 at 6:51 PM

Something like this?

public class ExampleBullet : Component
    {
        private float lifetime;

        public float BulletSpeed { get; set; }
        public TimeSpan Duration { get; set; }

        protected override void Update(float elapsedTime)
        {
            lifetime += elapsedTime;
            if (lifetime > Duration.TotalSeconds)
            {
                Scene.Remove(Parent);
                return;
            }

            var transform = Parent.Transform;
            var keyboardState = Keyboard.GetState();
            var direction = transform.Forward;

            if (keyboardState.IsKeyDown(Keys.A))
            {
                direction = transform.Left;
            }
            if (keyboardState.IsKeyDown(Keys.D))
            {
                direction = transform.Right;
            }
            if (keyboardState.IsKeyDown(Keys.S))
            {
                direction = transform.Forward;
            }
            if (keyboardState.IsKeyDown(Keys.W))
            {
                direction = transform.Backward;
            }

            transform.Translation += direction * BulletSpeed * elapsedTime;
            Parent.Transform = transform;
        }
    }
Dec 3, 2012 at 7:13 PM

The thing is, if i face left while standing, it will just shoot forward... =(

And now you can control the bullet again, if you hold down 'W' while you shot ,it will go forward all the sudden. 

Developer
Dec 3, 2012 at 7:36 PM

So it is kind of swinging around the player?

Dec 3, 2012 at 8:05 PM

Yeah you could state it like that.

It is 'controlable' if you shoot it, but I fixed it with the awesome boolean changeBulletDirection.

However I want to be able to shoot in the direction I'm facing when I'm standing still. 

Dec 5, 2012 at 1:51 PM

I'm still not really pleased how I fixed the direction thing.

I'm looking for a way to already define the direction of the bullet, when a new instance of the bullet is created.

private void OnKeyDown(object sender, KeyboardEventArgs e)
        {
            if (e.Key == Trigger)
            {
                
                var bullet = new Instance() { Template = BulletTemplate };
                bullet.Transform = Parent.Transform;
                Scene.Add(bullet);
                
            }
        }

 

So I'm looking for a way to give a direction to the bullet in the code above where a new instance is created.

Any ideas?