Using Github CopilotXChat in your Projects
So, you want to use the power of AI to speed things up?
Want to discover mysterious coding ways?
Wish to enter the Matrix?
Ok, that last one was a joke but you get the idea, using something like GitHub, take a breath, CopilotXChat [GHCPXC], assuming that is the correct name for it… can drastically speed up your coding endeavours, however, I find you need to be well versed in coding with whatever language you use, in our case, C#.
What do I mean with that?
I would argue you need to be somewhat an intermediate C# programmer to catch the mistakes GHCPXC creates, and also, understand how to get it to fix those errors too.
I will begin with a simple example using a Console app.
Now, I did generate the base code with GHCPXC, henceforth CPXC, but I had to fill in some details and modify the code a little, but essentially I have this:
click to show/hide code block
using System;
internal class Program
{
private static void Main(string[] args)
{
var person = new Person();
person.Name = "John";
person.Age = 20;
person.Introduce("Hot Chocolate");
var person2 = new Person();
person2.Name = "July";
person2.Age = 22;
person2.Introduce("");
var person3 = new Person();
person3.Name = "John";
person3.Age = 20;
person3.Introduce("Pineapples");
}
private class Person
{
public Person()
{
Name = null;
Age = 0;
}
public string? Name { get; internal set; }
public int Age { get; internal set; }
internal void Introduce(string v)
{
Console.WriteLine("Hi, my name is " + Name);
Console.WriteLine("I am " + Age + " years old");
if (v == "")
Console.WriteLine("I don't like anything");
else
Console.WriteLine("I like " + v);
Console.WriteLine();
}
}
}
The v variable was defined by CPXC
What I did next, was select the following lines:
var person = new Person();
person.Name = "John";
person.Age = 20;
person.Introduce("Hot Chocolate");
And selected the Ask Copilot
option in the right click context menu… [Alt+/]
And asked it to:
simplify this code
Which it then spat out the following code:
click to show/hide code block
using System;
internal class Program
{
private static void Main(string[] args)
{
var person1 = new Person("John", 20, "Hot Chocolate");
person1.Introduce();
var person2 = new Person("July", 22);
person2.Introduce();
var person3 = new Person("John", 20, "Pineapples");
person3.Introduce();
}
private class Person
{
public Person(string name, int age, string likes = "")
{
Name = name;
Age = age;
Likes = likes;
}
public string Name { get; internal set; }
public int Age { get; internal set; }
public string Likes { get; internal set; }
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name);
Console.WriteLine("I am " + Age + " years old");
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything");
else
Console.WriteLine("I like " + Likes);
Console.WriteLine();
}
}
}
Can you spot the differences? pretty neat right?
We can make it more neat…
Select the space above internal class Program
and press ALT+/
and ask:
add comments to this code
Which gave me:
click to show/hide code block
using System;
internal class Program
{
// Main method that starts execution.
private static void Main(string[] args)
{
var person1 = new Person("John", 20, "Hot Chocolate");
person1.Introduce();
var person2 = new Person("July", 22);
person2.Introduce();
var person3 = new Person("John", 20, "Pineapples");
person3.Introduce();
}
// Class defining a Person.
private class Person
{
// Constructor for the Person class.
public Person(string name, int age, string likes = "")
{
Name = name;
Age = age;
Likes = likes;
}
// The person's name.
public string Name { get; internal set; }
// The person's age.
public int Age { get; internal set; }
// The person's likes.
public string Likes { get; internal set; }
// Method to introduce the person.
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name);
Console.WriteLine("I am " + Age + " years old");
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything");
else
Console.WriteLine("I like " + Likes);
Console.WriteLine();
}
}
}
I was not happy with the granularity, and wanted to speed up adding comments to:
click to show/hide code block
// Create a new Person object and call the Introduce() method.
var person1 = new Person("John", 20, "Hot Chocolate");
person1.Introduce();
// Create another new Person object and call the Introduce() method.
var person2 = new Person("July", 22);
person2.Introduce();
// Create a third new Person object and call the Introduce() method.
var person3 = new Person("John", 20, "Pineapples");
person3.Introduce();
So I asked it to… [I skipped showing the snippet and added the end result to save repeated reading for the reader]
I asked it to add comments to:
public string Name { get; internal set; }
public int Age { get; internal set; }
public string Likes { get; internal set; }
And it got a little annoyed at me so it gave me this:
click to show/hide code block
using System;
internal class Program
{
// Main method that starts execution.
private static void Main(string[] args)
{
var person1 = new Person("John", 20, "Hot Chocolate"); // Create a new instance of a person with name, age, and likes arguments.
person1.Introduce(); // call the introduce method of person1 to print out its details.
var person2 = new Person("July", 22); // Create a new instance of a person with only the name and age arguments.
person2.Introduce(); // call the introduce method of person2 to print out its details.
var person3 = new Person("John", 20, "Pineapples"); // Create a new instance of a person with name, age, and likes arguments.
person3.Introduce(); // call the introduce method of person3 to print out its details.
}
// Class defining a Person.
private class Person
{
// Constructor for the Person class.
public Person(string name, int age, string likes = "")
{
Name = name; // Set the name of the person instance.
Age = age; // Set the age of the person instance.
Likes = likes; // Set the likes of the person instance (defaults to empty string).
}
// The person's name.
public string Name { get; internal set; }
// The person's age.
public int Age { get; internal set; }
// The person's likes.
public string Likes { get; internal set; }
// Method to introduce the person.
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name); // Prints out the person's name.
Console.WriteLine("I am " + Age + " years old"); // Prints out the person's age.
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything"); // Checks to see if the person doesn't have any likes, and prints accordingly.
else
Console.WriteLine("I like " + Likes); // Prints out the person's likes.
Console.WriteLine(); // Adds space between introductions.
}
}
}
Anyway, joking aside, note how much time that saved me, I did not need to explain what my code [or anyone else’s code for that matter] did, or how it works, it was done for me, and that can be minutes to hours for a longer chunk of code, and remember, you should be breaking things down to small chunks.
Moving on, now that we have a small idea or wait a moment…
click to show/hide code block
public Person(string name, int age, string likes = "")
{
Name = name; // Set the name of the person instance.
Age = age; // Set the age of the person instance.
Likes = likes; // Set the likes of the person instance (defaults to empty string).
}
I selected this block of code and asked it to:
add a height variable to the entire code
What do you think happened?
click to show/hide code block
// Class defining a Person.
private class Person
{
// Constructor for the Person class.
public Person(string name, int age, string likes = "", int height = 0)
{
Name = name; // Set the name of the person instance.
Age = age; // Set the age of the person instance.
Likes = likes; // Set the likes of the person instance (defaults to empty string).
Height = height; // Set the height of the person instance (defaults to 0).
}
// The person's name.
public string Name { get; internal set; }
// The person's age.
public int Age { get; internal set; }
// The person's likes.
public string Likes { get; internal set; }
// The person's height.
public int Height { get; internal set; }
// Method to introduce the person.
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name); // Prints out the person's name.
Console.WriteLine("I am " + Age + " years old"); // Prints out the person's age.
Console.WriteLine("I am " + Height + "cm tall"); // Prints out the person's height.
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything"); // Checks to see if the person doesn't have any likes, and prints accordingly.
else
Console.WriteLine("I like " + Likes); // Prints out the person's likes.
Console.WriteLine(); // Adds space between introductions.
}
}
Notice anything missing?
click to show/hide code block
var person1 = new Person("John", 20, "Hot Chocolate"); // Create a new instance of a person with name, age, and likes arguments.
See it yet?
include the height
I asked it to add the height on this section and got the following code:
click to show/hide code block
using System;
internal class Program
{
// Main method that starts execution.
private static void Main(string[] args)
{
var person1 = new Person("John", 20, "Hot Chocolate", 180); // Create a new instance of a person with name, age, height, and likes arguments.
person1.Introduce(); // call the introduce method of person1 to print out its details.
var person2 = new Person("July", 22); // Create a new instance of a person with only the name and age arguments.
person2.Introduce(); // call the introduce method of person2 to print out its details.
var person3 = new Person("John", 20, "Pineapples", 165); // Create a new instance of a person with name, age, height, and likes arguments.
person3.Introduce(); // call the introduce method of person3 to print out its details.
}
// Class defining a Person.
private class Person
{
// Constructor for the Person class.
public Person(string name, int age, string likes = "", int height = 0)
{
Name = name; // Set the name of the person instance.
Age = age; // Set the age of the person instance.
Height = height; // Set the height of the person instance.
Likes = likes; // Set the likes of the person instance (defaults to empty string).
}
// The person's name.
public string Name { get; internal set; }
// The person's age.
public int Age { get; internal set; }
// The person's height.
public int Height { get; internal set; }
// The person's likes.
public string Likes { get; internal set; }
// Method to introduce the person.
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name); // Prints out the person's name.
Console.WriteLine($"I am {Age} years old and I am {Height} cm tall."); // Prints out the person's age and height.
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything"); // Checks to see if the person doesn't have any likes, and prints accordingly.
else
Console.WriteLine("I like " + Likes); // Prints out the person's likes.
Console.WriteLine(); // Adds space between introductions.
}
}
}
You might notice a problem remaining…
Hi, my name is John
I am 20 years old and I am 180 cm tall.
I like Hot Chocolate
Hi, my name is July
I am 22 years old and I am 0 cm tall.
I don't like anything
Hi, my name is John
I am 20 years old and I am 165 cm tall.
I like Pineapples
July has a height issue, lets finish this section off and see how well CPXC does…
I selected:
click to show/hide code block
Console.WriteLine($"I am {Age} years old and I am {Height} cm tall."); // Prints out the person's age and height.
Asked:
add a null check for height and exclude it from the output
And got the most amusing fix:
click to show/hide code block
Hi, my name is John
I am 20 years old
and I am 180 cm tall.
I like Hot Chocolate
Hi, my name is July
I am 22 years old
and I don't have a height.
I don't like anything
Hi, my name is John
I am 20 years old
and I don't have a height.
I like Pineapples
Complete code:
click to show/hide code block
using System;
internal class Program
{
// Main method that starts execution.
private static void Main(string[] args)
{
var person1 = new Person("John", 20, "Hot Chocolate", 180); // Create a new instance of a person with name, age, height, and likes arguments.
person1.Introduce(); // call the introduce method of person1 to print out its details.
var person2 = new Person("July", 22); // Create a new instance of a person with only the name and age arguments.
person2.Introduce(); // call the introduce method of person2 to print out its details.
var person3 = new Person("John", 20, "Pineapples", null); // Create a new instance of a person with name, age, height, and likes arguments with height set to null.
person3.Introduce(); // call the introduce method of person3 to print out its details (height excluded).
}
// Class defining a Person.
private class Person
{
// Constructor for the Person class.
public Person(string name, int age, string likes = "", int? height = null)
{
Name = name; // Set the name of the person instance.
Age = age; // Set the age of the person instance.
Height = height; // Set the height of the person instance.
Likes = likes; // Set the likes of the person instance (defaults to empty string).
}
// The person's name.
public string Name { get; internal set; }
// The person's age.
public int Age { get; internal set; }
// The person's height (nullable).
public int? Height { get; internal set; }
// The person's likes.
public string Likes { get; internal set; }
// Method to introduce the person.
internal void Introduce()
{
Console.WriteLine("Hi, my name is " + Name); // Prints out the person's name.
Console.WriteLine($"I am {Age} years old");
if (Height.HasValue)
Console.WriteLine($"and I am {Height.Value} cm tall."); // Prints out the person's height if not null.
else
Console.WriteLine("and I don't have a height."); // Prints out that the person doesn't have a height if null.
if (string.IsNullOrEmpty(Likes))
Console.WriteLine("I don't like anything"); // Checks to see if the person doesn't have any likes, and prints accordingly.
else
Console.WriteLine("I like " + Likes); // Prints out the person's likes.
Console.WriteLine(); // Adds space between introductions.
}
}
}
I did notice I accidently left two John’s in there…
Moving on…
We now have a good idea of how involved you might need to be or how little you can be, but do not misunderstand, you do need to review the code, and ensure it is not doing something you did not expect, and always look for thrown exceptions added before you build and run.
Let’s try a MonoGame example, a brief one at that…
Spinning up a new MonoGame Desktop DirectX project, I asked CPXC to:
create a black pixel texture and add it to the content manager
inside the LoadContent()
method and received the following back:
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
blackPixelTexture = new Texture2D(GraphicsDevice, 1, 1);
blackPixelTexture.SetData(new Color[] { Color.Black });
// Add texture to content manager
Content.Load<Texture2D>("blackPixelTexture", blackPixelTexture);
// TODO: use this.Content to load your game content here
}
Which brought about some issues, but we can resolve those using just CPXC, select the first mention of blackPixelTexture using your right click on your mouse and select Ask Copilot
below it should show the text Explain and fix CS0103
click it.
We find it throws an error saying the solution was redacted…
This is a good example of the AI being completely useless but not by its own initiative… I found this issue to occur far too often.
Let’s select a fix using the VS Hover options… or ALT+Enter
fixes…
I selected the Generate Property option which added:
public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
// The following line
public Texture2D blackPixelTexture { get; private set; }
Which left a remaining error with the load method following… I selected the red underlined code Load<Texture2D>
and asked it to Explain and fix CS1501
which gave me the fix of:
Content.Load<Texture2D>("blackPixelTexture");
Which was wrong…
So I deleted it…
Your LoadContent()
should look like this so far:
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
blackPixelTexture = new Texture2D(GraphicsDevice, 1, 1);
blackPixelTexture.SetData(new Color[] { Color.Black });
// TODO: use this.Content to load your game content here
}
Let’s move along to the Draw()
method…
just after the TODO
comment, ask CPXC to:
Draw the black pixel at 30x scale in the centre of the screen
Which will spit out:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
var scale = 30f;
var position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
var origin = new Vector2(0.5f, 0.5f);
_spriteBatch.Begin();
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
_spriteBatch.End();
base.Draw(gameTime);
}
So, copy this and replace your original method… Optional to re-add the TODO
comment…
Running this code will give the following output:
If you thought I was done there, then you do not know my boredom levels…
Highlight the following lines:
_spriteBatch.Begin();
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
_spriteBatch.End();
And ask:
Make this sprite move in a radial circle randomly
which should spit out the following code:
// Define random offsets for the x and y axis from the center of the screen
var xOffset = new Random().Next(50, 150);
var yOffset = new Random().Next(50, 150);
// Calculate the new position of the sprite based on the elapsed game time
var angle = (float)(gameTime.TotalGameTime.TotalSeconds * Math.PI);
var newX = (float)(Math.Cos(angle) * xOffset) + (GraphicsDevice.Viewport.Width / 2);
var newY = (float)(Math.Sin(angle) * yOffset) + (GraphicsDevice.Viewport.Height / 2);
// Update the sprite position and redraw it
position = new Vector2(newX, newY);
_spriteBatch.Begin();
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
_spriteBatch.End();
Complete Draw()
code thus far:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
var scale = 30f;
var position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
var origin = new Vector2(0.5f, 0.5f);
// Define random offsets for the x and y axis from the center of the screen
var xOffset = new Random().Next(50, 150);
var yOffset = new Random().Next(50, 150);
// Calculate the new position of the sprite based on the elapsed game time
var angle = (float)(gameTime.TotalGameTime.TotalSeconds * Math.PI);
var newX = (float)(Math.Cos(angle) * xOffset) + (GraphicsDevice.Viewport.Width / 2);
var newY = (float)(Math.Sin(angle) * yOffset) + (GraphicsDevice.Viewport.Height / 2);
// Update the sprite position and redraw it
position = new Vector2(newX, newY);
_spriteBatch.Begin();
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
_spriteBatch.End();
base.Draw(gameTime);
}
Now, this is a little too jittery for our liking, so I highlighted the code that related to the drawing, and asked:
The movement is too jittery, can you smooth it out?
Which gave me:
var scale = 30f;
var position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
var origin = new Vector2(0.5f, 0.5f);
// Define random offsets for the x and y axis from the center of the screen
var xOffset = new Random().Next(50, 150);
var yOffset = new Random().Next(50, 150);
// Calculate the new position of the sprite based on the elapsed game time
var angle = (float)(gameTime.TotalGameTime.TotalSeconds * Math.PI);
var newX = (float)(Math.Cos(angle) * xOffset) + (GraphicsDevice.Viewport.Width / 2);
var newY = (float)(Math.Sin(angle) * yOffset) + (GraphicsDevice.Viewport.Height / 2);
// Smoothly interpolate between the current position and the new position
position = Vector2.Lerp(position, new Vector2(newX, newY), MathHelper.SmoothStep(0f, 1f, 0.5f));
// Update the sprite position and redraw it
_spriteBatch.Begin();
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
_spriteBatch.End();
Not an impressive improvement but you now have some slightly complex code to learn from… and it could not be simpler than our best friend:
add detailed comments to this code
With the Draw()
Method highlighted:
protected override void Draw(GameTime gameTime)
{
// Clear the screen to a visually appealing color
GraphicsDevice.Clear(Color.CornflowerBlue);
// Set scaling of the sprite
var scale = 30f;
// Set position of the sprite at the center of the screen
var position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
// Set origin of the sprite, specifying its alignment point at the center of the sprite texture
var origin = new Vector2(0.5f, 0.5f);
// Define random offsets for the x and y axis from the center of the screen
var xOffset = new Random().Next(50, 150);
var yOffset = new Random().Next(50, 150);
// Calculate the new position of the sprite based on the elapsed game time
var angle = (float)(gameTime.TotalGameTime.TotalSeconds * Math.PI);
var newX = (float)(Math.Cos(angle) * xOffset) + (GraphicsDevice.Viewport.Width / 2);
var newY = (float)(Math.Sin(angle) * yOffset) + (GraphicsDevice.Viewport.Height / 2);
// Smoothly interpolate between the current position and the new position
position = Vector2.Lerp(position, new Vector2(newX, newY), MathHelper.SmoothStep(0f, 1f, 0.5f));
// Begin drawing using spritebatch
_spriteBatch.Begin();
// Draw the black pixel texture using spritebatch with the settings defined above
_spriteBatch.Draw(blackPixelTexture, position, null, Color.White, 0f, origin, scale, SpriteEffects.None, 0f);
// End drawing using spritebatch
_spriteBatch.End();
// Call the base class's Draw method
base.Draw(gameTime);
}
Now, as this was simply a demonstration, you would not keep logical code
in the draw method, instead logical code
would be in the update method.
As CPXC is ever evolving, I may come back to this guide at some point.
Try to experiment and find issues to fix yourself, try highlighting all the code relating to the pixel and asking:
with the random movement, follow the mouse around the screen as the centre of the position
And have fun!
Happy Coding!