iT邦幫忙

0

(已解決)新手C#:迴圈使用疑問

https://ithelp.ithome.com.tw/upload/images/20210404/20133915Civ5Lqh8pH.jpg
想要放10顆球在裡面一起跑(不考慮碰球跟球之間的碰撞)

也就是picturebox4~12都要寫一樣的東西
試問這樣的迴圈寫法要如何著手?謝謝大家

目前寫了兩顆球用這種方式:

還有很多顆要重複寫一樣的東西,求大師指點要如何使用迴圈

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

3
japhenchen
iT邦超人 1 級 ‧ 2021-04-06 16:16:52
最佳解答

盡量不要用可視元件(Control)當做動畫元素,因為系統繪製Control的過程需要大量處理器及時間

以下是我草草寫的程式,沒用到DirectX/OpenGL
VS2019 + C# + .NetCore 3.1 + WinForm(你也可以一字不漏的抄也能在其他.net framework版本上跑,沒有系統依賴)

主程式只用到填滿Form的panel和timer,未用到其他元件或程式庫,程式下方的ball是一個自定class

(放在公司的雲端共享空間,請自行下載)
http://mail.jho.com.tw/owncloud/index.php/s/BwG5aTK8jN8Ej8L

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace pinBall
{
    public partial class Form1 : Form
    {
        List<ball> balls = new List<ball>();
        public Form1()
        {
            InitializeComponent();
        }

        private void drawballs(Graphics g)
        {
            foreach (var b in balls)
            {
                using (Brush sb = new SolidBrush(b.color))
                {
                    g.FillEllipse(sb, b.pos.X-b.r, b.pos.Y-b.r, b.R, b.R);                    
                }
                b.move(); // 把球球的下一步都算好
            }
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            // 產生10個球並存入 List of ball裡
            for (int b = 1; b <= 10; b++) 
            {
                //依Form畫面尺寸準備10個小球
                balls.Add(new ball(Width, Height, 50));
            }
            timer1.Interval = 100; // 每秒10 FPS
            timer1.Enabled = true; // 打開畫球的Timer 
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            drawballs(e.Graphics); // 畫球
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            panel1.Invalidate(); // 重新繪製畫面(會引發panel1_Paint事件)
        }
    }
    
    // 主程式結束
    
    // 以下是球球所需要的class , 讓每個球都有自己的屬性及運算軌跡方式
    class ball
    {
        public Color color { get; set; }
        public PointF pos { get; set; }
        public int directionX { get; set; }
        public int directionY { get; set; }
        public int R { get; set; }
        public float r { get; set; } //半徑
        private int pWidth { get; set; }
        private int pHeight { get; set; }
        public ball(int width, int height, int ballR)
        {
            Random rnd = new Random(Guid.NewGuid().GetHashCode());
            pos = new PointF(rnd.Next(ballR, width - ballR), rnd.Next(ballR, height - ballR));
            directionX = 5 - rnd.Next(10);
            directionY = 5 - rnd.Next(10);
            R = ballR; // 直徑 
            r = R / 2;
            pWidth = width;
            pHeight = height;
            color = Color.FromArgb(255, rnd.Next(255), rnd.Next(255), rnd.Next(255));
        }
        public void move()
        {
            // 如果下一步超過畫面 ... 大於寬高或小於0的話,則折返,方法就是直接把X跟Y移動值正負顛倒就對了!
            if (pos.X + directionX + R >= pWidth)
            {
                directionX *= -1;
            }
            if ((pos.X + directionX) - R <= 0)
            {
                directionX = Math.Abs(directionX);
            }
            if (pos.Y + directionY + R >= pHeight)
            {
                directionY *= -1;
            }
            if ((pos.Y + directionY) - R <= 0)
            {
                directionY = Math.Abs(directionY);
            }
            pos = new PointF(pos.X+ directionX,pos.Y + directionY);
        }
    }
}

沒打開doublebuffer,所以會有些閃爍的,請見諒

看更多先前的回應...收起先前的回應...

https://ithelp.ithome.com.tw/upload/images/20210406/20117954i63luvFGCd.jpg

真要動畫跑的順,不閃爍的話,你還非得要用到Unity(或全手工跑OpenGL/DirectX)來做不可哩,不過那對新手而言實在是有點高牆,連我自己都學藝不精,所以不敢現醜啦

0805cyc iT邦新手 4 級 ‧ 2021-04-06 16:34:02 檢舉

https://ithelp.ithome.com.tw/upload/images/20210406/20133915kFWacSqnqM.jpg

您好,現在已將這四個picturebox寫入陣列裡,但是活動範圍變得很奇怪,每顆球幾乎都各自在紅色框框的範圍內撞

for(int Ball=0; Ball < 4; Ball++)
{
switch (i % 2)
{
case 0:
if (pictureBoxArray[Ball].Left < panel1.Width - pictureBoxArray[Ball].Width)
{
pictureBoxArray[Ball].Left = pictureBoxArray[Ball].Left + 10;
}
else
{
i = i + 1;
}
break;
case 1:
if (pictureBoxArray[Ball].Left > 0)
{
pictureBoxArray[Ball].Left = pictureBoxArray[Ball].Left - 10;
}
else
{
i = i - 1;
}
break;
}

            switch (j % 2)
            {
                case 0:
                    if (pictureBoxArray[Ball].Top < panel1.Height - pictureBoxArray[Ball].Height)
                    {
                        pictureBoxArray[Ball].Top = pictureBoxArray[Ball].Top + 10;

                    }
                    else
                    {
                        j = j + 1;

                    }
                    break;
                case 1:
                    if (pictureBoxArray[Ball].Top > 0)
                    {
                        pictureBoxArray[Ball].Top = pictureBoxArray[Ball].Top - 10;
                    }
                    else
                    {
                        j = j - 1;

                    }
                    break;
            }
        }
0805cyc iT邦新手 4 級 ‧ 2021-04-06 16:36:48 檢舉

也想請問
pictureBoxArray[0] = pictureBox1;
pictureBoxArray[1] = pictureBox2;
pictureBoxArray[2] = pictureBox3;
pictureBoxArray[3] = pictureBox4;
因為我有49張圖,想問這種還有別種寫法嗎(迴圈)?

不要用pictureBox這類"Control可視元件",你只要畫面上有十個可視元件,卡頓會讓你叫不敢

如果真的非這樣做不可,那就

List<PictureBox> pictures = new List<PictureBox>();
for(int x = 1 ; x<=49;x ++){
    PictureBox pb = new PictureBox();
    pb.Left = 10;
    pb.Top = 20;
    pb.Parent = Form1;  
    pictures.Add(pb);
}

0805cyc iT邦新手 4 級 ‧ 2021-04-06 23:25:10 檢舉

想請問.parent是指甚麼?https://ithelp.ithome.com.tw/upload/images/20210406/20133915RxMU7H1Zr8.jpg

親代窗口,放這個picturebox的Control,通常為form或panel

不過我程式寫錯了,你改一下

pb.Parent = this ;

this.Controls.Add(pb);

我最開頭的程式修改一下,不用放panel,把form的屬性doublebuffer 設成enable

程式改成以下,Timer.interval 改成 30 (33 FPS)都不會閃爍了!

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace pinBall
{
    public partial class Form1 : Form
    {
        List<ball> balls = new List<ball>();
        public Form1()
        {
            InitializeComponent();
        }

        private void drawballs(Graphics g)
        {
            foreach (var b in balls)
            {
                using (Brush sb = new SolidBrush(b.color))
                {
                    g.FillEllipse(sb, b.pos.X - b.r, b.pos.Y - b.r, b.R, b.R);
                }
                b.move(); // 下一步
            }
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            for (int b = 1; b <= 10; b++) // 產生10個球並存入 List of ball裡
            {
                balls.Add(new ball(Width, Height, 50));
            }
            timer1.Interval = 30;
            timer1.Enabled = true;
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            drawballs(e.Graphics);
        }
    }
    class ball
    {
        public Color color { get; set; }
        public PointF pos { get; set; }
        public int directionX { get; set; }
        public int directionY { get; set; }
        public int R { get; set; }
        public float r { get; set; } //半徑
        private int pWidth { get; set; }
        private int pHeight { get; set; }
        public ball(int width, int height, int ballR)
        {
            Random rnd = new Random(Guid.NewGuid().GetHashCode());
            pos = new PointF(rnd.Next(ballR, width - ballR), rnd.Next(ballR, height - ballR));
            directionX = 5 - rnd.Next(10);
            directionY = 5 - rnd.Next(10);
            R = ballR; // 直徑 
            r = R / 2;
            pWidth = width;
            pHeight = height;
            color = Color.FromArgb(255, rnd.Next(255), rnd.Next(255), rnd.Next(255));
        }
        public void move()
        {
            if (pos.X + directionX + R >= pWidth)
            {
                directionX *= -1;
            }
            if ((pos.X + directionX) - R <= 0)
            {
                directionX = Math.Abs(directionX);
            }
            if (pos.Y + directionY + R >= pHeight)
            {
                directionY *= -1;
            }
            if ((pos.Y + directionY) - R <= 0)
            {
                directionY = Math.Abs(directionY);
            }
            pos = new PointF(pos.X + directionX, pos.Y + directionY);
        }
    }
}
0805cyc iT邦新手 4 級 ‧ 2021-04-10 14:07:06 檢舉

謝謝您~

1
小魚
iT邦大師 1 級 ‧ 2021-04-04 18:00:09

picturebox可以在後端產生,
其實你寫在WinForm也是會有一些後端的程式碼,
可以到另外一個檔案(通常是desinger.cs)去看picturebox的宣告.
用一個PictureBox的陣列放這些picturebox,
就可以用迴圈來產生跟運動了.

0805cyc iT邦新手 4 級 ‧ 2021-04-04 18:23:11 檢舉

https://ithelp.ithome.com.tw/upload/images/20210404/20133915yZRDIWJr4n.jpg
是這個意思嗎?
https://social.msdn.microsoft.com/Forums/zh-TW/acd26f78-0ba8-4987-879f-5872bba71770/3553121839-38364picturebox-2356525033-3849921015-303402183938988?forum=233

迴圈的部分我不是很清楚本人沒碰過幾次..可以加以解說指點嗎謝謝不好意思

小魚 iT邦大師 1 級 ‧ 2021-04-05 18:36:25 檢舉

類似這樣吧...

List<PictureBox> pictureList;
function 建構式()
{
   pictureList = new List<PictureBox>();
}

function xxx()
{
    for(int i=0;i<?;i++)
    {
       PictureBox picture = new PictureBox();
       picture.Left = xxx;
       picture.Top = xxx;
       ...
       ...
       plctureList.add(picture);
    }
}

之類的,
不過要把picturebox加到control中.

我要發表回答

立即登入回答