I am trying to make a little game coded in c#, the game involves moving enemies.
These enemies are spawned in using the following code, this code is used multiple times to spawn multiple enemies.
private void EventHandler(Action<object, EventArgs> spawnBox)
{
Random randomPlek = new Random();
int xPlek;
xPlek = randomPlek.Next(1000, 1100);
int yPlek;
yPlek = (randomPlek.Next(0, 8)) * 100;
var picture = new PictureBox
{
Name = "pictureBoxLM",
Size = new Size(150, 100),
SizeMode = PictureBoxSizeMode.StretchImage,
BackColor = Color.Transparent,
Location = new Point(xPlek, yPlek),
Image = Leeuwenmier,
};
this.Controls.Add(picture);
}
The problem is that when trying to make them move or collide, Visual Studio can't find the name and gives an error. This is the code i used for collision:
if(PbMier.Bounds.IntersectsWith(pictureBoxLM.Bounds))
{
// some actions
}
How can I call the spawned picturebox in the code without getting an error?
WinForms controls have names, but that doesn't mean you can access them using that name as a C# identifier.
Your PictureBox only has a named reference within EventHandler(), namely picture, but once control leaves that method that reference goes out of scope.
You need to find the controls again, or find another way to reference the generated controls.
So either:
var allPictureBoxes = this.Controls.Find("PictureBoxLM");
foreach (var pictureBox in allPictureBoxes)
{
// ...
}
Or put this on your form:
List<PictureBox> pictureBoxList = new List<PictureBox>();
And then in the EventHandler();
this.Controls.Add(picture);
pictureBoxList.Add(picture);
After which you can use this for your collision detection:
foreach (var pictureBox in pictureBoxList)
{
// ...
}
Related
I want to get the object that created in struct. Showing the codes will explain it better though.
private void Obstacle()
{
obstacle_pos_x = obstacle_random_x.Next(1000);
obstacle_pos_y = obstacle_random_y.Next(700);
picture = new PictureBox
{
Name = "pictureBox" + obstacle_numb,
Size = new Size(32, 32),
Location = new Point(obstacle_pos_x,obstacle_pos_y),
BackColor = Color.Black,
};
this.Controls.Add(picture);
}
This is the struct inside of Obstacle method. As you can see this method creates pictureboxes and I want to pull them into KeyPressEvents. Like, if I press W, all the pictureboxes that created by struct has to move -10(y axis).
else if (e.KeyCode == Keys.W)
{
y -= chrspeed;
obstacle_numb++;
Obstacle();
for (int i = 0; i <= obstacle_numb; i++)
{
}
}
Well this the event. But it just creates pictureboxes. For loop is empty, because I couldn't figure out what to do. I simply want to do something like that,
picture+obstacle_numb.Location = new Point(x,y); (I need this picture+obstacle_numb combination.)
But also know that it is imposible. foreach came up to my mind but I don't know how to use it. Maybe something like this can work if fixed.
foreach(PictureBox objects from picture) //It doesn't work too.
I'm stuck right now and waiting for your help. Thanks in advance.
The easiest would be to iterate all the child controls that are of type PictureBox:
...
foreach (var pict in this.Controls.OfType<PictureBox>())
{
// pict is now a Picturebox, you can access all its properties as you have constructed it
// the name was constructed that way: Name = "pictureBox" + obstacle_numb,
if (pict.Name != "pictureBox1")
{
pict.Location = new Point(pict.X, pict.Y-10);
}
}
I'm using SFML for C#. I want to create a BackgroundImage Sprite and then start drawing it with an Agent, represented as a Circle, on top of it like that:
static void Main(string[] args)
{
Window = new RenderWindow(new VideoMode((uint)map.Size.X * 30, (uint)map.Size.Y * 30), map.Name + " - MAZE", Styles.Default);
while (Window.IsOpen)
{
Update();
}
}
static public RenderWindow Window { get; private set; }
static Map map = new Map(string.Format(#"C:\Users\{0}\Desktop\Maze.png", Environment.UserName));
static public void Update()
{
Window.Clear(Color.Blue);
DrawBackground();
DrawAgent();
Window.Display();
}
static void DrawAgent()
{
using (CircleShape tempCircle = new CircleShape
{
FillColor = Color.Cyan,
Radius = 15,
Position = new Vector2f(30, 30),
Origin = new Vector2f(30, 30),
Scale = new Vector2f(.5f, .5f)
})
{
Window.Draw(tempCircle);
}
}
static private Sprite BackgroundImage { get; set; }
static void DrawBackground()
{
if (BackgroundImage == null)
BackgroundImage = GetBackground();
Window.Draw(BackgroundImage);
}
static Sprite GetBackground()
{
RenderTexture render = new RenderTexture((uint)map.Size.X * 30, (uint)map.Size.Y * 30);
foreach (var point in map.Grid.Points)
{
RectangleShape pointShape = new RectangleShape(new Vector2f(30, 30));
switch (point.PointType)
{
case PointType.Walkable:
pointShape.FillColor = Color.White;
break;
case PointType.NotWalkable:
pointShape.FillColor = Color.Black;
break;
case PointType.Start:
pointShape.FillColor = Color.Red;
break;
case PointType.Exit:
pointShape.FillColor = Color.Blue;
break;
}
pointShape.Position = new Vector2f(point.Position.X * 30, point.Position.Y * 30);
render.Draw(pointShape);
}
Sprite result = new Sprite(render.Texture);
result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
result.Scale = new Vector2f(1, -1);
return result;
}
Everything works as intended when I start it, but after a few seconds, around the time when process memory reaches 70MB, BackgroundImage turns into completely white sprite. If I change the type of BackgroundImage and GetBackground() to RenderTexture, return "render" object and then change DrawBackground() function like this
void RenderBackground()
{
if (BackgroundImage == null)
BackgroundImage = GetBackground();
using (Sprite result = new Sprite(BackgroundImage.Texture))
{
result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
result.Scale = new Vector2f(1, -1);
Window.Draw(result);
}
}
then the background sprite doesn't turn white, but storing entire RenderTexture, instead of Sprite and then constantly creating new Sprite objects every time we call RenderBackground() function seems like a bad idea.
Is there any way for GetBackground() function to return a Sprite which won't turn white once the function's local "render" variable is destroyed?
You're not completely off with your assumptions. Simplified, SFML knows two types of resources:
Light resources are small objects that are quick to create and destroy. It's not that bad to just drop them and recreate them later. Typical examples would be Sprite, Sound, Text, and basically most SFML classes.
Heavy resourcces are often big objects or objects requiring file access to create or use. Typical examples would be Image, Texture, SoundBuffer, and Font. You shouldn't recreate these and instead keep them alive while you need them. If they're disposed too early, light resources using them will fail in some way or another.
A sprite's texture turning white is – as you've discovered – a typical sign of the assigned texture being freed/disposed.
There are many different approaches to this, but I'd suggest you create some kind of simple resource manager that will load resources just in time or just return them, if they're loaded already.
I haven't used SFML with C# and I haven't really touched C# for quite a while, but for a simple implementation you'd just have a Dictionary<string, Texture>. When you want to load a texture file like texture.png, you look whether there's a dictionary entry with that key name. If there is, just return it. If there isn't, create the new entry and load the texture, then return it.
I'm out of practice, so please consider this pseudo code!
private Dictionary<string, Texture> mTextureCache; // initialized in constructor
public Texture getTexture(file) {
Texture tex;
if (mTextureCache.TryGetValue(file, out tex))
return tex;
tex = new Texture(file);
mTextureCache.add(file, tex);
return tex;
}
// Somewhere else in your code:
Sprite character = new Sprite(getTexture("myCharacter.png"));
If your heavy resource is a RenderTexture, you just have to ensure that it stays alive as long as it's used (e.g. as a separate member).
It turned out the answer was simpler that I expected. All I had to to was create new Texture object and then make a Sprite out of it. So instead of
Sprite result = new Sprite(render.Texture);
I wrote
Sprite result = new Sprite(new Texture(render.Texture));
Now garbage collector doesn't dispose Sprite's texture
For a button click two new gameObjects are created and the second one is setting as a child of first one and first one is child of attached object. But it is not working / showing on hierarchy but the created object is not appearing in game window.
Here is the code:
public void CreateTextButtonClick(string text)
{
Debug.Log("Hey starting of this..");
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myText");
txtObj.transform.SetParent(this.transform, false);
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform);
//Attach Text,RectTransform, an put Font to it
Text txt = pan.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = Color.blue;
//image
//GameObject back = new GameObject("image");
//back.transform.SetParent(txtObj.transform);
//Image i = back.AddComponent<Image>();
Debug.Log("Hey its done..");
}
What can be a solution to achieve this multi level object making?
You used SetParent properly in the first Object you want to be child of your Canvas by passing false to it which causes it to keep its local orientation:
GameObject txtObj = new GameObject("myText");
txtObj.transform.SetParent(this.transform, false);
But you did not do this for the "text" Object that will be child of the object above:
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform);
You can fix that by also passing false to it so that the UI component to keep its local orientation. Unrelated but I also think you should add a RectTransform the parent object too. Add RectTransform to anything that will be under the Canvas.
Your new code:
public void CreateTextButtonClick(string text)
{
Debug.Log("Hey starting of this..");
//Create Canvas Text and make it child of the Canvas
GameObject txtObj = new GameObject("myText");
txtObj.AddComponent<RectTransform>();
txtObj.transform.SetParent(this.transform, false);
GameObject pan = new GameObject("text");
pan.transform.SetParent(txtObj.transform, false);
//Attach Text,RectTransform, an put Font to it
Text txt = pan.AddComponent<Text>();
txt.text = text;
Font arialFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.font = arialFont;
txt.lineSpacing = 1;
txt.color = Color.blue;
}
In your commented code, it looks like you plan to add another child object with Image component too and I can already see that mistake there. Do not forget to do this too. You must otherwise, you will have issues. This applies to everything you plan to place under the Canvas.
I have a problem with C# game.
There are a matrix with randomly-placed wolves and raccoons. When user click the "new game" button I need to generate new positions for animals and redraw my canvas (PicureBox).
So, I have no problem with generating, but position of animals in pictureBox do not change (but it shows, when generates first time).
Also I tried to create several pictureBox with animal icons (and move it in game process), but they does not shown (I had added it in this.Controls, but nothing happened). Maybe there is some form manager like web-inspector in browsers to see what exactly happens?
I do not want to move a project to WPF Application.
Pieces of code below:
private static Bitmap _gameArea = new Bitmap(_gameAreaSize, _gameAreaSize);
Painted event (create a table):
private void drawTable(object sender, PaintEventArgs e)
{
Pen p = new Pen(Color.LightSlateGray);
var g = Graphics.FromImage(_gameArea);
for (int i = 0; i < Configs.MatrixSize + 1; i++)
{
float coord = i * _cellSize;
if (coord.Equals(_gameAreaSize))
coord = _gameAreaSize - 1;
g.DrawLine(p, coord, 0, coord, _gameAreaSize);
g.DrawLine(p, 0, coord, _gameAreaSize, coord);
}
pictureBox1.BackgroundImage = _gameArea;
g.Dispose();
}
For game start and for new game click:
GameLogic.InitGame(); // change positions, without threads
RefreshMap();
RefreshMap:
private static void RefreshMap()
{
var g = Graphics.FromImage(_gameArea);
g.Clear(Color.White);
g.Dispose();
RefreshRacoons();
RefreshWolves();
}
I tried to make smth like this:
private static void RefreshMap()
{
var sync = SynchronizationContext.Current;
new Thread(__ =>
sync.Post(_ =>
{
var g = Graphics.FromImage(_gameArea);
g.Clear(Color.White);
g.Dispose();
RefreshRacoons();
RefreshWolves();
}, null)).Start();
}
And refresh (with synContext and without it)
private static void RefreshRacoons()
{
foreach (var racoon in GameLogic.Racoons)
{
var g = Graphics.FromImage(_gameArea);
g.DrawImage(_racoonImg,
_cellSize * racoon.X + _offsetX,
_cellSize * racoon.Y + _offsetY,
_animalIconWidth,
_animalIconHeight);
g.Dispose();
}
}
private static void RefreshWolves()
{
var sync = SynchronizationContext.Current;
foreach (var wolf in GameLogic.Wolves)
{
new Thread(__ =>
sync.Post(_ =>
{
var g = Graphics.FromImage(_gameArea);
g.DrawImage(_wolfImg,
_cellSize * wolf.X + _offsetX,
_cellSize * wolf.Y + _offsetY,
_animalIconWidth,
_animalIconHeight);
g.Dispose();
}, null)).Start();
}
}
Thank you!
My usual disclaimer is this: Windows Forms is not the right environemnt for game development. Turnbased singleplayer or hotseat multiplayer games can work, if you do not overdo it with animations. But Solitaire and Minesweeper are pretty much the upper limit of what is possible.
There are a number of Dedicateed Display technologies/Engines avalible for game development. But in the end anything that allows you direct, low level drawing access and has a game loop of some sort should work out. While you can fake that in Windows Forms to some degree, you basically end up reinventing the wheel when you have a fully filled up car with keys in the Ignition just standing there.
For .NET Framework, the dated soluation is the XNA Framework. But if you go for .NET Core, there is at least 3 official solutions:
https://www.microsoft.com/net/learn/apps/gaming
Most of wich work with Mono and some might even work with the .NET Framework (I have not tried it myself).
I have a method which finds relationships between two objects, if it exists, I would like to draw two Lineshapes to the link. I have started to implement the first lineshape, however whenever I test the code the lines persist. I have tried multiple methods (as you can see) and these do not refresh the canvas for the new lines to be drawn.
private void DrawRelationshipLines()
{
_canvas = new ShapeContainer {Parent = panelCredentialsVisualisation};
//These methods below do not redraw the canvas
_canvas.Shapes.Remove(_tableinfoLine);
_canvas.Shapes.Clear();
_canvas.Refresh();
_canvas.Update();
//
List<string> relationships = lvSelectedTableInfoCredentialsIntersection.GetAllRelationships();
if (relationships.Capacity == 0)
return;
foreach (string context in relationships)
{
Label contextLabelName = GetLabelByName(context);
_tableinfoLine = new LineShape
{
Parent = _canvas,
BorderWidth = 2,
BorderColor = Color.BlueViolet,
StartPoint = new Point(lblselectedTableinfo.Right, lblselectedTableinfo.Top + 10),
EndPoint = new Point(contextLabelName.Left, contextLabelName.Top + 10)
};
}
The code works fine in searching for relationships and drawing them, however I'd like to be able to clear the canvas before drawing a different relationship, is this possible?
Thanks if anyone can help.
Moving _canvas = new ShapeContainer {Parent = panelCredentialsVisualisation}; outside of the method made this work. Seems like initializing a new ShapeContainer each time was causing issues.