Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 4

Проблемы с таймером или с логикой?

03.08.2011, 01:05. Показов 2202. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Господа, помогите начинающему:

Есть объект DynamicGameObject, его тип содержит статический список всех подобных объектов List GameObjects, а так же список объектов, которым сообщена функция движения (DynamicGameObject.Move.To(x,y,speed)) List MoveToObjects
MoveToObjects обрабатывается в отдельном потоке, который создается функцией Movement(iDel) в статическом конструкторе. Поток проходит по списку, получает следующий объект, обрабатывает его движение за некоторый дискретный отрезок времени (iDel)

Прогоняю тест на одном объекте с заданными параметрами, вычисляю теоретическое время движения, потом сравниваю с реальным и... Естественно не получаю равенства. Все бы ничего если бы разница была в сотых долях секунды (при iDel = 10 миллисекунд), но разница тем больше, чем меньше iDel.
Так
при задержке в 10 миллисекунд:
Теоретическое время движения 10 сек
Практическое время движения 15,59 сек
при задержке в 100 миллисекунд:
Практическое время движения 10,83 сек
при задержке в 200 миллисекунд:
Практическое время движения 9,95 сек
при задержке в 300 миллисекунд:
Практическое время движения 10,31 сек

(Последний тест так же показывает, что время теста не является строго убывающей прогрессией зависящей от iDel. Наиболее точный результат, при точке финиша 100, получаем при iDel 310. Но точность пропадает если поставить координату например 80. При iDel меньше 5 программа так же ведет себя некорректно.)

Помогите мне найти ошибку в логике программы или посоветуйте другой способ реализации движения.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Xml;
 
 
 
 
class Server
{
    static void Main(string[] args)
    {
        //Console.Clear();
        //AsyncServer ConnectionServer = new AsyncServer(11100);
        Game Process = new Game();
        Console.ReadLine();
    }
}
 
class Game
{
    public Game()
    {
        //Random Rand = new Random();
        for (int i = 0; i < 1; i++) 
        {
            //new DynamicGameObject(Rand.Next() % 10, Rand.Next() % 10, "Object "+i).Move.To(Rand.Next() % 100, Rand.Next() % 100);
            new DynamicGameObject(0.0, 0.0).Move.To(0.0, 100.0);
 
        
        }
    }    
}
 
 
 
class GameObject
{
    public static List<GameObject> GameObjects = new List<GameObject>();
    public double x{ get; set; }
    public double y{ get; set; }
    public string name;
    
    public GameObject() {}
    public GameObject(double _x, double _y, string _name = null)
    {
        GameObjects.Add(this);
        name = _name == null ? "Object": _name;
        x = _x;
        y = _y;
        
        Console.WriteLine("{0} has created. Coords is ({1},{2})",name,x, y);
    }    
}
 
class DynamicGameObject : GameObject
{
    public class MoveToInfo
    {
        private DynamicGameObject MTO; //Parent Object
        public MoveToInfo(DynamicGameObject parent) { this.MTO = parent; }
        
        
        private double X { get; set; }
        private double Y { get; set; }
        private double sinA { get; set; }
        private double cosA { get; set; }
        private double speed { get; set; }
        private DateTime StartTime;
        private DateTime FinishTime;
        
        public int MStatus = 0;     /* 
                                     0 - not moved
                                     1 - added to list
                                     2 - 1st itr. of cycle passed
                                     3 - ready to delete
                                    */
 
    //=====================================Movement phases===================================
        //==============Phase 1: Add to List================
        public void To(double MTX, double MTY, double speed=10.0)
        {
            this.speed = speed;
           
            if (MTX != MTO.x || MTY != MTO.y)
            {                
                double _X = MTX - MTO.x;
                double _Y = MTY - MTO.y;
                double _Z = Math.Sqrt(_X * _X + _Y * _Y);
                double f = _Z / speed;
                
                X = MTX;
                Y = MTY;
                sinA = _Y / _Z;
                cosA = _X / _Z;
                
                Console.WriteLine("Theoretical time of motion {0} sec", f.ToString()); 
                
                if (MStatus != 1)
                {
                    lock (SomeMoveToObjects) 
                    {
                        SomeMoveToObjects.Add(MTO); 
                    }
                }
                MStatus = 1;
            }
        }
        //==============Phase 2: Step by Step===============
        public void Step(double Span)
        {
            if (MStatus == 1)
            {
                StartTime = DateTime.Now;
                Console.WriteLine("{3} started moving to {0},{1} in {2}", X, Y, StartTime, MTO.name);
                MStatus = 2;
            }
            MTO.x += (Math.Abs(MTO.x - X) >= Math.Abs(cosA * speed * Span)) ? cosA * speed * Span : X - MTO.x;
            MTO.y += (Math.Abs(MTO.y - Y) >= Math.Abs(sinA * speed * Span)) ? sinA * speed * Span : Y - MTO.y;
            Console.WriteLine("{0} move to ({1},{2})", MTO.name, MTO.x, MTO.y);
            if (MTO.x == X && MTO.y == Y)
            {
                MStatus = 3;
                //Console.WriteLine("{0} ready to finish in {1} for {2}", MTO.name, DateTime.Now, StartTime - DateTime.Now);
            }
            
        }
        //==============Phase 3: Remove from List===========
        public bool ReadyToRemove()
        {
            if (MStatus == 3) 
            {                
                FinishTime = DateTime.Now; 
                Console.WriteLine("{0} has finished to {2},{3} in {1} for {4}", MTO.name, FinishTime, MTO.x, MTO.y, StartTime - FinishTime); 
                MStatus = 0; 
                return true; 
            }
            else { return false; }
        }
    //=======================================================================================
    }
    public static void Movement(long iDel)
    {
        Timer ticker = new Timer(new TimerCallback((object state)=>
        {
            lock (MoveToObjects)
            {
                lock (SomeMoveToObjects) { MoveToObjects.AddRange(SomeMoveToObjects); SomeMoveToObjects.Clear(); }
                MoveToObjects.ForEach(MTO => MTO.Move.Step(iDel*0.001));
                MoveToObjects.RemoveAll(MTO => MTO.Move.ReadyToRemove());
            }
        }),null,0,iDel);
    }   
    
    public static List<DynamicGameObject> MoveToObjects = new List<DynamicGameObject>();
    public static List<DynamicGameObject> SomeMoveToObjects = new List<DynamicGameObject>();
    public static List<DynamicGameObject> ReadyToDeleteObjects = new List<DynamicGameObject>();
 
    public MoveToInfo Move;
    
    //===================================Конструкторы======================================
    static DynamicGameObject()
    {
        Movement(20);
    }
    public DynamicGameObject() : base() { Move = new MoveToInfo(this); }
    public DynamicGameObject(double _x, double _y, string _name = null): base(_x, _y, _name) 
    {
        Move = new MoveToInfo(this);    
    }
    //===================================================================================
        
}
Добавлено через 10 часов 41 минуту
Ну хорошо. Забудем о прошлом коде. Вот та же проблема в укороченной форме:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Threading;
 
namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime ST = DateTime.Now, ET=DateTime.Now;
            double x =0.0, X=100.0;
            long iDel = 31;
            bool flag=true;
            Timer t = new Timer((object s) =>
            {
                if (flag) { ST = DateTime.Now; flag = false; }
                if (x < X){ x += iDel * 0.01;}
                else {ET = DateTime.Now; Console.WriteLine(ET - ST);}            
            }, null, 0, iDel);
            Console.ReadLine();
        }
    }
}
Лучший результат опять получаем при iDel = 31 или 310

Извините не разобрался как остановить таймер, если можно помогите заодно и с этой проблемой.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
03.08.2011, 01:05
Ответы с готовыми решениями:

Проблемы с логикой
string src = &quot;There are many big and small libraries everywhere in our country&quot;; if (src.Contains(&quot;There&quot;) |...

Проблемы с таймером
Нужна помощь новичку) МК ATtiny13, режим таймера СТС, предделитель 8, количество счетов 9 Не понимаю, почему при достижении таймером...

Проблемы с таймером
Доброго дня есть програмка которая дожна запускать батник каждые 900 сек i:=i-1; lbl1.Caption:='Почта будет отправлена...

7
67 / 67 / 9
Регистрация: 18.04.2011
Сообщений: 124
03.08.2011, 02:31
Какая-то жесть, кстати, да. Я думал, что проблема в том, что таймер просто отрабатывает слишком часто - и действительно, при интервале в 5 миллисекунд у него адская погрешность, а при высоком (200-300) он довольно точен. Но почему при интервале 29-31 он точнее, чем при интервале 40?)

В общем, не смог придумать этому внятное объяснение и сделал тупой вариант в лоб - зато точный. Так подойдет?
C#
1
2
3
4
5
6
7
8
9
10
11
            double x = 0.0, X = 100.0;
            double iDel = 40;
            Stopwatch ST = new Stopwatch();
            ST.Start();
            do
            {
                x += iDel * 0.01;
                Thread.Sleep((int)iDel);
            } while (x < X);
            Console.WriteLine(ST.Elapsed);
            Console.ReadLine();
Тоже не идеал, конечно - при интервале в 5 он засекает порядка 11 секунд. Улучшить результат можно, заменив на что-нибудь Thread.Sleep (при низком времени задержки его включение может занимать больше, чем сама задержка), но перевести Thread.SpinWait в миллисекунды адекватно не смог.
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 4
03.08.2011, 11:52  [ТС]
Цитата Сообщение от somethingrotten Посмотреть сообщение
Какая-то жесть, кстати, да. Я думал, что проблема в том, что таймер просто отрабатывает слишком часто - и действительно, при интервале в 5 миллисекунд у него адская погрешность, а при высоком (200-300) он довольно точен. Но почему при интервале 29-31 он точнее, чем при интервале 40?)

В общем, не смог придумать этому внятное объяснение и сделал тупой вариант в лоб - зато точный. Так подойдет?
C#
1
2
3
4
5
6
7
8
9
10
11
            double x = 0.0, X = 100.0;
            double iDel = 40;
            Stopwatch ST = new Stopwatch();
            ST.Start();
            do
            {
                x += iDel * 0.01;
                Thread.Sleep((int)iDel);
            } while (x < X);
            Console.WriteLine(ST.Elapsed);
            Console.ReadLine();
Тоже не идеал, конечно - при интервале в 5 он засекает порядка 11 секунд. Улучшить результат можно, заменив на что-нибудь Thread.Sleep (при низком времени задержки его включение может занимать больше, чем сама задержка), но перевести Thread.SpinWait в миллисекунды адекватно не смог.
Да, я тоже задумывался о stopwatch, но честно говоря надеялся, что на это все-таки способен таймер...

Накропал такой вот корявенький, но зато точный примерчик:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Threading;
using System.Diagnostics;
 
namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime ST = DateTime.Now, ET=DateTime.Now;
            double x =0.0, X=300.0;
            Stopwatch Watch = new Stopwatch();
            bool flag=true;
            bool fflag = true;
            while (fflag) 
            {
                if (flag) { ST = DateTime.Now; Watch.Start(); flag = false; }
                if (x < X) { if (Watch.ElapsedMilliseconds > 1) { x += (Watch.ElapsedMilliseconds * 0.01); Watch.Restart();} }
                else { ET = DateTime.Now; Console.WriteLine(ET - ST); fflag = false; }        
            
            }         
            
            Console.ReadLine();
        }
    }
}
Но сдается мне он тратит на себя несколько больше ресурсов, чем нужно...


Такой вопрос - где можно посмотреть на реализацию стандартных методов С#? Очень бы хотелось посмотреть например на Thread.Sleep, чтобы успокоить свою совесть и увидеть что там тоже используется пустой цикл для ожидания...
0
67 / 67 / 9
Регистрация: 18.04.2011
Сообщений: 124
03.08.2011, 12:33
3y3, нет-нет, это плохо: у вас каждые две-три миллисекунды часы делают рестарт. У меня ваш код грузит процессор аж на 50%) Stopwatch я использовал просто как замену вашему Date.Time - так удобней замерять время.
Описание метода можно посмотреть, например, здесь. Thread.Sleep не использует пустой цикл для ожидания - просто поток, в котором он выполняется, отдает свое процессорное время и не участвует в работе. Процессор на нем отдыхает)

Может, стоит зайти с другого конца: а зачем вам выражать приращение x как iDel * 0.01? Если x у вас - координата, то перебирая разные значения iDel, вы будете менять только плавность движения - скорость, время и расстояние останутся одинаковыми (ну, в пределах погрешности). Может, сделать просто x плюс константа, скажем, 0,1?
1
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 4
03.08.2011, 14:19  [ТС]
Цитата Сообщение от somethingrotten Посмотреть сообщение
3y3, нет-нет, это плохо: у вас каждые две-три миллисекунды часы делают рестарт. У меня ваш код грузит процессор аж на 50%) Stopwatch я использовал просто как замену вашему Date.Time - так удобней замерять время.
Описание метода можно посмотреть, например, здесь. Thread.Sleep не использует пустой цикл для ожидания - просто поток, в котором он выполняется, отдает свое процессорное время и не участвует в работе. Процессор на нем отдыхает)

Может, стоит зайти с другого конца: а зачем вам выражать приращение x как iDel * 0.01? Если x у вас - координата, то перебирая разные значения iDel, вы будете менять только плавность движения - скорость, время и расстояние останутся одинаковыми (ну, в пределах погрешности). Может, сделать просто x плюс константа, скажем, 0,1?
В общем-то dx у меня и так константа, просто от опыта к опыту я проверяю, какая константа лучше...

(Тут я перестал писать ответ на сообщение и решил проверить еще один кусок кода)

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Threading;
using System.Diagnostics;
 
namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime ST = DateTime.Now, ET=DateTime.Now, SST = DateTime.Now;
            double x =0.0, X=300.0;
            bool flag=true;
            bool fflag = true;
            Timer t = new Timer((object s) =>
            {
                    if (flag) { SST = DateTime.Now; flag = false; }
                    if (x < X) { x += DateTime.Now.Subtract(ST).Ticks * 1E-7 * 10; ST = DateTime.Now; }
                    else if(fflag) { ET = DateTime.Now; Console.WriteLine(ET - SST); fflag=false;}
            }, null, 0, 1);            
            Console.ReadLine();
        }
    }
}
Точность подобного исполнения меня вполне устраивает. Как я понял проблема была как раз в том, что приращение было задано константой, а в данном случае DateTime.Now.Subtract(ST).Ticks имеет каждый раз куда меньшее расхождение с теоретически прошедшим временем. Я примерно то же самое пытался описать в stopwatch, но так сказать не нашел нужных слов =)

Буду признателен, если Вы поможете мне деликатно остановить таймер в этом примере.

Кстати на счет описания метода Thread.Sleep - msdn это первое место куда я лезу чтобы узнать о методе, но я имел в виду именно исполняемый код метода, а не его описание. Где найти подобную информацию (Я так понимаю можно копать даже в фреймфорке у себя на компе)
0
67 / 67 / 9
Регистрация: 18.04.2011
Сообщений: 124
03.08.2011, 15:20
3y3,
Цитата Сообщение от 3y3 Посмотреть сообщение
DateTime.Now.Subtract(ST).Ticks имеет каждый раз куда меньшее расхождение с теоретически прошедшим временем
На самом деле нет. Проверьте - DateTime.Now.Subtract(ST).Ticks*0.000001 возвращает всегда одинаковое значение (у меня это 0.15625). Впрочем, если точность вас устраивает - да будет) (хотя с Thread.Sleep можно было точнее).

Остановка таймера - поскольку у вас Threading.Timer, его можно только убить методом извне:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Program
    { 
        static Timer t;
        static void Main(string[] args)
        {
            DateTime ST = DateTime.Now, ET=DateTime.Now, SST = DateTime.Now;
            double x =0.0, X=300.0;
            bool flag=true;
            bool fflag = true;
            t = new Timer((object s) =>
            {
                    if (flag) { SST = DateTime.Now; flag = false; }
                    if (x < X) { x += DateTime.Now.Subtract(ST).Ticks * 1E-7 * 10; ST = DateTime.Now; }
                    else if(fflag) { ET = DateTime.Now; Console.WriteLine(ET - SST); fflag=false; StopTimer();}
            }, null, 0, 1);            
            Console.ReadLine();
        }
        static void StopTimer() 
        {
            t.Dispose();
         }      
    }
"Подробную информацию" по Thread.Sleep - это вам куда-то в области WinAPI, на уровень управления потоками) Но я думаю, MSDN вполне можно доверять, когда она говорит, что слип не грузит процессор)
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
03.08.2011, 16:54
C#
1
2
3
4
public static void Sleep(int millisecondsTimeout)
{
    SleepInternal(millisecondsTimeout);
}
C#
1
2
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SleepInternal(int millisecondsTimeout);
Смотреть можно программой .NET Reflector
2
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 4
04.08.2011, 10:41  [ТС]
Цитата Сообщение от somethingrotten Посмотреть сообщение
(хотя с Thread.Sleep можно было точнее).
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Program
{
    static void Main(string[] args)
    {        
        double x = 0.0, X = 115.0;
        DateTime ST, ET = DateTime.Now;
        while (x < X)
        {
            ST = DateTime.Now;
            Thread.Sleep(1);
            x += DateTime.Now.Subtract(ST).Ticks * 1E-7 * 10;
        }
        Console.WriteLine(DateTime.Now.Subtract(ET));
        Console.ReadLine();
    }
}
Ну допустим =) Подобный вариант, а так же вариант со SpinWait показывают одинаковые результаты.
Проблема явно исчерпала себя...

Добавлено через 17 часов 43 минуты
http://blogs.msdn.com/b/ericli... etime.aspx

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Threading;
using System.Diagnostics;
 
class Program
{
    static void Main(string[] args)
    {        
        double x = 0.0, X = 19.0;
        TimeSpan TT;
        Stopwatch Watch = new Stopwatch();
        Watch.Start();
        while (x < X)
        {
            TT = Watch.Elapsed;
            Thread.Sleep(10);
            x += (Watch.Elapsed.Subtract(TT).Ticks * 1E-7) * 10;
        }
        Watch.Stop();
        Console.WriteLine(Watch.Elapsed);
        Console.ReadLine();
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
04.08.2011, 10:41
Помогаю со студенческими работами здесь

Проблемы с таймером
Проблема заключается в следующем. Имеется такая вот подпрограмма: ;Подпрограмма временной задержки time_delay: ;Настройка и запуск...

Проблемы с таймером ATTiny13A
PCI0_ISR: PUSH TMP SBIC DevisePin,DevisePinN //I?iionoeou anee aeo a ii?oo i?euai RJMP PCI0_H OUT TCNT0,Null...

Проблемы с таймером 2 в stm8l151k4
В регистре TIM2_SMCR самый младший бит из битов TS упорно не хочет выставляться. Пишем например: TIM2_SMCR = 0x18; в итоге имеем: ...

Проблемы с таймером 4 у stm8s103f2
День добрый. Наступил на грабли на ровном месте. Начинаю разбираться с STM8. Использую &quot;ST Visual Develop&quot; + Cosmic8 +...

(Решено)STM32f100 Проблемы с таймером 2.
Здравствуйте уважаемые форумчане. Начал изучение и один проект на STM32, добрался до таймеров, и возникла проблема. Таймер 2 не хочет...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru