Смекни!
smekni.com

Система координат канви (стр. 2 из 3)

// не закрашувати

Canvas->Font->Color = clBlack; Canvas->TextOutA(x,y,ms); // вивести текст }

1.2 Вивід зображень за допомогою пікселів

Малювати на канві можна різними способами. Перший варіант — малювання по пикселам. Для цього використовується властивість канви Pixels. Цією властивістю є двовимірний масив Canvas->Pixels[intX][int Y], який відповідає за кольори канви. Наприклад, Canvas->PixeIs[10][20] відповідає кольору пиксела, 10-го зліва і 20-го зверху. З масивом пикселов можна звертатися як з будь-якою властивістю: змінювати колір, задаючи пикселу нове значення, або визначати його колір по значенню, що зберігається в нім. Наприклад, Canvas->PixeIs[10][20]= clBlack — це завдання пикселу чорного кольору.

Давайте спробуємо намалювати графік деякої функції F(X) наканве компоненту Imagel, якщо відомий діапазон її зміни Ymax і Ymin і діапазон зміни аргументу Xmin і Хтах. Це можна зробити такою процедурою:

float X,Y; // координати функції

int PX,PY; // координати пикселов

for (РХ = 0: РХ <= Imagel->Width; Рх++)

//Х- координата, відповідна пикселу з координатою РХ

X = Xmin + РХ * (Xmax - Xmin) / Imagel->Width;

Y = F(X); //PY - координата пиксела, відповідна координаті Y

PY = imagel->Height - (Y - Ymin)*Imagel->Height/(Ymax-Ymin); //Устанавливается чорний колір вибраного пиксела

Imagel->Canvas->Pixels[PX][PY] = clBlack;}

У цьому коді вводяться змінні X і Y, що є значеннями аргументу і функції, а також змінні РХ і PY, що є координатами пикселов, відповідними X і Y. Сама процедура складається з циклу по всіх значеннях горизонтальної координати пикселов РХ компоненту Imagel. Спочатку вибране значення Рхнересчитиваєтсявсоответствующєєзначенієх. Потім проводиться виклик функції і визначається її значення Y. Це значення перераховується у вертикальну координату пиксела PY. І на закінчення колір пиксела з координатами (РХ, PY) встановлюється чорним.

Спробуйте створити відповідне застосування і подивитися, як воно працює. Хай для простоти ми орієнтуватимемося на функцію sin(X), для якої Xmin=0, Хmax=4pi (2 періоди в радіанах), Ymin=-1, Ymax=l.

Почніть новий проект, помістіть на нього компонент Image і кнопку з написом «Намалювати», в обробник події OnClick якою запишіть код, аналогічний приведеному вище, але що конкретизує функцію:

#definePi 3.14159

float X, Y; // координати функції

int РХ, PY; // координати пикселов

for (РХ = 0: РХ <- Imagel->Width; PX++)

{//X - координата, відповідна пикселу з координатою РХ

X = РХ * 4 * Pi / imagel->Width;

Y = sin(X); //PY - координата пиксела, відповідна координаті У

PY = Imagel->Height - (Y+1) * Imagel->Height / 2; //Устанавливается чорний колір вибраного пиксела

Imagel->Canvas->Pixels(PX][PY] = clBlack; }

1.3 Збереження конфігурації в файлах ini

|Файли .ini - це текстові файли, призначені для зберігання інформації про настройки різних програм. Інформація у файлі логічно групується в розділи, кожен з яких починається оператором заголовка, поміщеним в квадратні дужки. Наприклад [Desktop|]. У рядках, наступних за заголовком, міститься інформація, що відноситься до даного розділу, у формі:

<ключ>=<значення>

[dBASE| Files|]

Driver32=C|:&bsol;WINDOWS&bsol;SYSTEM&bsol;odbcjt32.dll

Файли .ini, якправило, зберігаютьсявкаталозі Windows|, якийможназнайтизадопомогоюфункції GetWindowsDirectory|.

У C++Builder|роботузфайлами .ini найпростішездійснюватизадопомогоюствореннявпрограміоб'єктутипу TIniFile|. Цей тип описаний в модулі inifiles|, який треба підключати до програми оператором uses| (автоматично це не робиться).

При створенні об'єкту типу TlniFile| в нього передається ім'я файлу .ini, з яким він зв'язується. Файл повинен існувати до створення об'єкту.

Для запису значень ключів існує багато методів: WriteString|, WriteInteger|, WriteFloat|, |і ін. Кожен з них записує значення відповідного типу. Оголошення всіх цих методів дуже схожі. Наприклад:

void| fastcall| WriteString| (const| AnsiString| Section|

const| AnsiString| Ident|, const| AnsiString|Value|);

void| fastcall| Writelnteger| (const| AnsiString| Section|

const| AnsiString| Ident|, int| Value|);

Увсіхоголошеннях Section| - розділфайлу, Ident| - ключцьогорозділу, Value| - значенняключа. Якщовідповіднийрозділабоключвідсутнійуфайлі, вінавтоматичностворюється.

Єаналогічніметодичитання: ReadString|, Readlnteger|, ReadFloat|, ReadBool|іін. Наприклад:

AnsiString| fastcall| ReadString| (const| AnsiString| Section|

const| AnsiString| Ident|, const| AnsiString|Default|);

int| fastcall| Readlnteger| (const| AnsiString| Section|

const| AnsiString| Ident|, int| Default|);

Методи повертають значення ключа| розділу Section|. Параметр Default|визначає значення, що повертається у випадку, якщо у файлі не вказано значення відповідного ключа.

Перевірити наявність значення ключа можна методом ValueExists|, в який передаються імена розділу і ключа. Метод DeleteKey| видаляє з файлу значення вказаного ключа у вказаному розділі. Перевірити наявність у файлі необхідного розділу можна методом SectionExists|. Метод EraseSection| видаляє з файлу вказаний розділ разом зі всіма його ключами. Є ще ряд методів, які ви можете подивитися у вбудованій довідці C++Builder|.

Подивимося на прикладі, як все це можна використовувати для установки програми, запам'ятовування її настройоки і для видалення програми.

Зробіть просте тестову програму. Перенесіть на форму три кнопки Button| і діалог FontDialog|. Перша кнопка (назвіть її BInst| і задайте напис Install|) імітуватиме установку програми. Точніше, не саму установку, оскільки копіювати файли з загрузочної дискети ми не будемо, а тільки створення файлу .ini у каталозі Windows|. Друга кнопка (назвіть її BUnlnst| і задайте напис Unlnstall|) імітуватиме видалення програми. Тут ми не видалятимемо саму програму з диска, а тільки видалимо з каталога Windows| наш файл, а третя кнопка (назвіть її BFont| і задайте напис Font|) дозволятиме змінювати ім'я шрифту, використовуваного у формі, і забезпечить запам'ятовування цього шрифту у файлі .ini, щоб надалі при запуску програми можна було читати цю настройку і задавати її формі.

Розділ 2. Практична частина

2.1 Код гри

#include <vcl.h>

#pragma hdrstop

#include "UBilliard.h"

#include <math.h>

#include "inifiles.hpp"

#pragma package(smart_init)

#pragma resource "*.dfm"

// розмір столу

int const w = 600;

int const h = 300;

class TGameStatus {

protected: char* gsStatus;

public:

void gsBegin(){

gsStatus="qsBegin"; }

void gsGame(){

gsStatus="gsGame"; }

void gsGameOver(){

gsStatus="gsGameOver"; }};

TGameStatus *GameStatus = new TGameStatus;

struct TPlayer{

int balls; };

class TLose{

public:

int x,y;

int ballsInside;

int R;

void Draw();};

class TBall { public:

float x,y, dx,dy;

int R;

bool exists, stopped ;

int col,ID,count;

TList *Items;

void Draw();

void Stop();

bool InLose(int &Number);

void outFrom(TBall b);

TBall CollisedWith();

~TBall();};

class TCue {

public:

bool Visible;

TBall ToBall;

float Angle, energy;

void draw();

void Hit();};

void TCue::Hit(){

TBall *ToBall;

ToBall->dx =-cos(Angle)*energy;

ToBall->dy = -sin(Angle)*energy;

ToBall->stopped = False;

Visible = False;}

class TBilliardTable{

public:

int Width, Height, Left, Right, Top, Bottom;

TList* Ball;

TList* Lose;

TCue Cue;

void Draw();};

float GetAngToXY( pBall b ;float hitX, hitY){

float dx, dy, d;

dx = b->x - hitX;

dy = b->y - hitY;

d = sqrt(dx*dx+dy*dy);

if(dy>0)

Result = arccos(dx/d);

else Result = -arccos(dx/d); }

void TBall::outFrom(pBall:b){

{ AB,

aa, bb,

V1, V2,

aPrXx, aPrXy,

aPrYx, aPrYy,

aPrX, aPrY,

bPrXx, bPrXy,

bPrYx, bPrYy,

bPrX, bPrY,

alfaA, betaA, gammaA,

Extended alfaB, betaB, gammaB;

if(b == NULL ) exit;

AB = sqrt(sqr(x-b->x)+sqr(y-b->y));

V1 = sqrt(dx*dx+dy*dy);

V2 = sqrt(b->dx*b->dx+b->dy*b->dy);

aPrXx = 0;

aPrXy = 0;

bPrXx = 0;

bPrXy = 0;

////////-ball #1-

if(V1>0 ) {

if((b->y-y)>0

alfaA = arccos((b->x-x)/AB);

else alfaA = -arccos((b->x-x)/AB);

if(dy>0

then gammaA = arccos(dx/V1)

else gammaA = -arccos(dx/V1);

betaA = gammaA-alfaA;

aPrX = V1*cos(betaA);

aPrY = V1*sin(betaA);

aPrXx = aPrX*cos(alfaA);

aPrXy = aPrX*sin(alfaA);

aPrYx = aPrY*sin(alfaA);

aPrYy = aPrY*cos(alfaA); }

//////////////-ball #2-

if(V2>0 ) {

if((y-b->y)>0

then alfaB = arccos((x-b->x)/AB); //=alfaA+pi

else alfaB = -arccos((x-b->x)/AB);

if(b->dy>0)

then gammaB = arccos(b->dx/V2);

else gammaB = -arccos(b->dx/V2);

betaB = gammaB-alfaB;

bPrX = V2*cos(betaB);

bPrY = V2*sin(betaB);

bPrXx = bPrX*cos(alfaB);

bPrXy = bPrX*sin(alfaB);

bPrYx = bPrY*sin(alfaB);

bPrYy = bPrY*cos(alfaB); }

dx = ((dx - 2*aPrXx) + bPrXx)*mu; // = mu*(bPrXx - aPrXx)

dy = ((dy - 2*aPrXy) + bPrXy)*mu; // = mu*(bPrXy - aPrXy)

b->dx = ((b->dx - 2*bPrXx) + aPrXx)*mu; // = mu*(aPrXx - bPrXx)

b->dy = ((b->dy - 2*bPrXy) + aPrXy)*mu; // = mu*(aPrXy - bPrXy)}

void InitSound(){

pcm->wFormatTag = WAVE_FORMAT_PCM;

pcm->nChannels = 1;

pcm->nSamplesPerSec = 44100;

pcm->nAvgBytesPerSec = 2*44100;

pcm->nBlockAlign = 2;

pcm->wBitsPerSample = 16;

pcm->cbSize = 0;

WaveOut = 0;

open_status = waveOutOpen(&WaveOut, 0, &pcm, Form1->Handle,

0, callback_Window)}

float CalCulateAngle(){

{ int i, j;

pLose ToLz, lz;

pBall nearestBall, Bl, b;

float hitX, hitY;

minAng, a2Lz, minD,

float dx, dy, a, minDist, d;

minDist = 1.7e+308;

minD = minDist;

with BilliardTable do

{ for( j = 0; j <=Lose->Count-1; j ++)

for( i = 0; i <=Ball->Count-1; i ++)

{ lz = Lose->Items[j];

b = Ball->Items[i];

if(! b->exist ) continue;

d = sqrt(sqr(b->x-lz->x)+sqr(b->y-lz->y));

if(d < minDist )

{ minDist = d;

ToLz = lz;

Bl = b; } }

if((Bl == NULL) ) exit;

dx = Bl->x - ToLz->x;

dy = Bl->y - ToLz->y;

d = sqrt(dx*dx+dy*dy);

if((dy)>0

a2Lz = arccos(dx/d);

else a2Lz = -arccos(dx/d);

hitX = Bl->x + cos(a2Lz)*Bl->R;

hitY = Bl->y + sin(a2Lz)*Bl->R;

minAng = 1.7e+308;

for( i = 0; i <=Ball->Count-1; i ++)

{ b = Ball->Items[i];

if((b->ID == Bl->ID) || (not b->exist)

continue;

a = GetAngToXY(b, hitX, hitY);

if(abs(a2Lz-a) < minAng )

{ minAng = abs(a2Lz-a);

nearestBall = b;

Result = a; } }

for( i := 0 to Ball.Count-1 do

begin

b := Ball.Items[i];

if (b.ID = Bl.ID) or (not b.exist)

continue;

d := sqrt(sqr(b.x-Bl.x)+sqr(b.y-Bl.y));

if d < minD then

begin

minD := d;

nearestBall := b;

end;

end;

dx := Bl.x - ToLz.x;

dy := Bl.y - ToLz.y;

d := sqrt(dx*dx+dy*dy);

if (dy)>0

a2Lz := arccos(dx/d)

else a2Lz := -arccos(dx/d);

hitX := Bl.x + cos(a2Lz)*Bl.R;

hitY := Bl.y + sin(a2Lz)*Bl.R;

dx := nearestBall.x - hitX;

dy := nearestBall.y - hitY;

d := sqrt(dx*dx+dy*dy);

if (dy)>0

then a := arccos(dx/d)

else a := -arccos(dx/d);

Result := a;}

void ComputerMove(){

Cue->visible = True;

CompAngle = CalculateAngle;

if(CompAngle > Cue->angle)

CompMove = 1;

else CompMove = -1;}

void TBilliardTable::Draw(){

{ int i;

pBall *b;

pLose *lz;

char* WhoIsIt;

Canvas->Brush->Color = clBlack;

Canvas->Pen->Color = clBlack;

Canvas->Rectangle(0,0,Width, Height);

Canvas->Brush->Color = $336699;

Canvas->Pen->Color = clYellow;

Canvas->Rectangle(BilliardTable->Left - LoseSize, BilliardTable->Top - LoseSize,

BilliardTable->Right + LoseSize, BilliardTable->Bottom + LoseSize);

Canvas->Brush->Color = clGreen;

Canvas->Rectangle(BilliardTable->Left, BilliardTable->Top, BilliardTable->Right, BilliardTable->Bottom);

Canvas->Pen->Color = clYellow;

Canvas->Pen->Color = clBlack;

Canvas->Ellipse(BilliardTable->Left + (3 * BilliardTable->Width / 4)-2,

BilliardTable->Top + (BilliardTable->Height / 2)-2,

BilliardTable->Left + (3 * BilliardTable->Width / 4)+2,

BilliardTable->Top + (BilliardTable->Height / 2)+2);

Canvas->Brush->Color = $336699;

Canvas->Font->Color = clYellow;

Canvas->Font->Style = [];

if(Player == 0

then WhoIsIt = "Игрок";

if(Player == 1

then WhoIsIt = "Компьютер";

Canvas->TextOut(BilliardTable->Left+30, dh + 1,

"Ход: "+WhoIsIt+"а");