|
|
|
|
|
اموزش مقدماتی کامل برای مبتدیان http://www.schoolnet.ir/tutorial/vb/ اشاره : يك بازي كامپيوتري را روي كامپيوترتان اجرا ميكنيد. فعلاً كارت گرافيك شما روي اسلاتAGP سوار ميشود، پردازشگر سلرون داريد و ... پس از چند ماه يا چند سال كامپيوتر جديدي ميخريد. اكنون اسلات كارت گرافيكي شما PCI Express است و يك پردازشگر 64 بيتي داريد. همان بازي را روي اين كامپيوتر هم نصب و اجراميكنيد! شايد به نظر طبيعي ميآيد كه همه چيز بايد همينطور باشد. اما چگونه يك بازي روي كامپيوترهايي با تراشهها و سختافزارهاي مختلف و گاه فناوري متفاوت اجرا ميشود؟ APIهاي گرافيكي يا همان رابطهاي برنامهنويسي، بخش بزرگي از اين مشكل را حل ميكنند و امكانات گسترده ديگري را نيز در اختيار برنامهنويسان و توسعهدهندگان بازي و برنامههاي چندرسانهاي قرارميدهند. OpenGL وDirectX، دو مجموعه API گرافيكي و صوتي هستند كه براي آسانتر ساختن توسعه بازيها و نرمافزارهاي چندرسانهاي طراحي شدهاند. API گرافيكي چيست؟ API درواقع بين برنامه و سختافزاري كه برنامه روي آن اجرا ميشود، نقش يك هماهنگكننده را دارد و مانند پلي ميان سختافزار و نرمافزار ارتباط ايجادميكند. يعني برنامهنويس كدهايي مينويسد كه دادههاي گرافيكي خود را به وسيله دستورهاي استانداردي به درايور API ميفرستد نه مستقيماً به خود سختافزار. سپس درايوري كه شركت سازنده سختافزار توليدكرده است، اين كداستاندارد توليدشده را به فرمت بومي و ويژهاي كه براي آن مدل خاص سختافزار قابل شناسايي است، ترجمه ميكند. Microsoft DirectX شركت مايكروسافت در سال 1995 DirectX را ساخته و توسعه دادهاست. اين نرمافزار شامل مجموعه يكپارچهاي از ابزارهاي برنامهنويسي است كه به توسعهدهندگان امكان ميدهد انواع مختلف نرمافزارهاي مالتيمديا را روي پلتفرم ويندوز توليد كنند. DirectX به برنامهاي كه بر پايه آن طراحي شده امكان ميدهد به آساني قابليتهاي سختافزار كامپيوتر را شناسايي كند و پارامترهاي برنامه را با آن هماهنگ سازد. DirectX شامل APIهايي است كه دسترسي به بخشهاي ويژهاي از سختافزار مانند تراشههاي شتابدهنده گرافيك سهبعدي و كارت صوتي را ميسرميكند. اين APIها كنترل توابع سطح پايين، يعني نزديك به سختافزار، شامل شتابدهنده گرافيكي دو بعدي، پشتيباني از دستگاههاي ورودي مانند دسته بازي، صفحهكليد و ماوس، و كنترل ميكس و خروجي صدا را انجام ميدهند. DirectX 7.0 در سال 1999 با شش كامپوننت عرضه شد كه عبارت بودند از: Direct3D،DirectDraw ،DirectSound ،DirectPlay ،DirectInput و DirectMusic. در اواخر سال 2000 ميلادي، DirectX 8.0 عرضه شد كه در آن كامپوننتهاي DirectSound و DirectMusic با هم ادغام شدند و با نام كامپوننت Direct Audio معرفي شدند. Direct3D و DirectDraw نيز با هم ادغام شدند و يك كامپوننت با نام DirectX Graphics را ساختند. DirectShow نيز به صورت يك API جداگانه پيادهسازي شد و به يكي از كامپوننتهاي DirectX تبديل گرديد. DirectX 9.0 در ژانويه سال 2003 عرضه شد. ويژگيهاي خاص اين نسخه عبارتند از: - قابليتهاي صوتي جديد در DirectSound - سختافزار رندركننده ويديويي با شتاب بيشتر - بهبود قابليت برنامهريزي گرافيكي APIهاي همه كامپوننتهاي DirectX برپايه COM يا Component Object Model هستند. در ادامه به بررسي هفت كامپوننت DirectX 9.0 ميپردازيم كه عبارتند از: DirectDraw ،Direct3D ،DirectShow ،DirectSound ،DirectMusic ،DirectInput و DirectPlay. 1- DirectDraw DirectDraw، كامپوننتي ويژه طراحي دوبعدي است كه به برنامهنويس اجازه ميدهد مستقيماً به حافظه كارت گرافيك دسترسي يابد، صحنهها و فريمها را با هم تركيب نمايد يا bitmapها را در آنجا ذخيره كند. همچنين، براي برنامهها امكان دسترسي به سختافزارهاي ويژه نمايش را مستقل از نوع سختافزار فراهم ميكند. هر برنامه كاربردي DirectDraw الگوي يكساني دارد كه عبارت است از: - ايجاد يك شي - شروع حلقه - انتقال به مانتيور - پايان حلقه - پاك كردن آن شي منظور از واژه <يك شي> ميتواند هر تصوير دوبعدياي باشد و منظور از حلقه، حلقهاي است كه در برنامهنويسي هنگام تكرار منظم دستهاي از دادهها يا دستورها به كار ميبريم. تصوير ايجادشده پس از مدتي پاك ميشود و جاي خود را به تصوير ديگري ميدهد. 2- Direct3D اين كامپوننت، دسترسي به توابع رندركننده گرافيك سهبعدي تعبيه شده در بيشتر كارتهاي گرافيك را فراهم ميكند. Direct3D يك API سطح پايين سهبعدي است كه به نرمافزار امكان ميدهد مستقل از سختافزار، با سختافزار شتابدهنده ارتباط برقرار كند. لايهاي كه براي توسعهدهندگان بازي و گرافيك كامپيوتري امكان طراحي و ساخت بازيها را مستقل از سختافزار كامپيوترها فراهم ميكند، لايهاي به نام Hardware Abstraction Layer) HAL) است. HAL با قابليتهايي كه به صورت گسترده در سختافزارهاي گرافيك سهبعدي پيادهسازي شدهاند ارتباط ايجاد ميكند و به سازندگان امكانميدهد درايورهايي را توليد كنند كه لايه HAL را به سختافزار پيوند دهد. اين كار باعث ميشود برنامههاي كاربردي Direct 3D بدون اينكه براي نوع خاصي از قطعه سختافزاري نوشته شده باشد، از ويژگيهاي بخشهاي خاص آن قطعه سختافزاري بهرهببرد. در شكل يك چگونگي ارتباط لايه HAL با سختافزار و نرمافزارهاي مرتبط نشان داده شده است. شكل - چگونگي ارتباط لايه HAL با كارت گرافيك و نرمافزارهاي مرتبط http://www.shabakeh-mag.com/Data/Gallery/s63_gpl_1_s.jpg همانگونه كه در شكل يك، نشان داده شده، نرمافزار بازي بالاترين سطح است و پس از آن كامپوننتهاي ترسيم دوبعدي و سه بعدي، يعني DirectDraw و Direct3D قرار دارند. لايه HAL يك رابط ميان كامپوننتهاي DirectX و كارت گرافيك است. در سيستم رندر Direct3D، ساختار اشياي سهبعدي پيش از آنكه شتابدهنده سهبعدي، يك صحنه سهبعدي را رندر نمايد و آن را به مانيتور منتقل كند، به وسيله CPU پردازش ميشود. نسخه ششم كامپوننت Direct3D از قابليتهاي كارتهاي گرافيك جديدتر پشتيباني مينمايد و در هر گذر، چندين بافت را با هم رندر ميكند. كاهش زمان رندر به استفاده از نقشه بافتها نياز دارد. اين نسخه تكنيكهايي براي افزودن جلوهاي واقعيتر به صحنههاي سه بعدي را نيز دربردارد. مانند anistropic filtering كه عنصر عمق را به trilinear filtering و نقشه برجستهسازي ميافزايد كه موجب ايجاد شباهت بيشتر بافتها و نيز منابع نور تابيده شده بر سطوح مسطح با نمونههاي واقعي آنها ميشود. نسخه هفتم DirectX نسبت به نسخههاي پيش از خود بيست درصد سريعتر و شامل چند ويژگي ديگر بود. مهمترين آنها پشتيباني از تغييرات شتاب سختافزاري و نوردهي (T&L) به وسيله اغلب كارتهاي گرافيك سهبعدي آنزمان به ويژه كارتهايي است كه برپايه تراشههاي nVidia Geforce 256 و S3 Savage 2000 ساخته شدهاند. از زماني كه T&L عرضه شد، وقتگيرترين وظيفه CPU هنگام اجراي بازيهاي پيشرفته به شتابدهنده سهبعدي داده شد و بخش بزرگي از ظرفيت پردازنده اصلي به كارهاي ديگر مانند هوشمصنوعي بازي اختصاص داده شد و توسعهدهندگان بازي توانستند رندر را با جزئيات بيشتر انجام دهند و جلوههاي ويژه پيچيدهتري را در بازيها بهكار ببرند. 3- DirectShow اين كامپوننت از بسياري از فرمتهاي صوتي و ويديويي شامل AVI ،MPEG ،ASF ،WMA/WMV ،DV و MP3 و DirectX پشتيباني ميكند و روي ويندوزهاي 98، 2000، اكسپي و نرمافزار اينترنت اكسپلورر عرضه شده است.DirectShow پروسه كارهاي مالتيمديا مانند نمايش فايل ويديويي را به مجموعهاي از مراحل كه با نام filter شناخته ميشوند تقسيم ميكند. فيلترها تعدادي pin ورودي و خروجي دارند كه آنها را به هم متصل ميكند. طراحي كلي سازوكار اتصال به اين صورت است كه فيلترها ميتوانند به روشهاي مختلف به هم متصل شوند كه هر نوع از اين اتصالها به معني انجام دادن يك كار است و توسعهدهندگان نرمافزار ميتوانند افكتهاي خود يا فيلترهاي ديگري را به بخشي از اين گراف براي انجام كار ويژهاي بيفزايند. گراف فيلتر DirectShow به صورت گسترده در ضبط صدا و فيلم، و ويرايش آنها به كار ميرود. شكل - يك گراف فيلتر كه كار نمايش يك فايل MPEG را نشان ميدهد. در شكل دو، يك گراف نمايش براي فايل فيلمي از نوع MPEG نشان داده شده است. برنامههاي كاربردي DirectShow، براي پردازش دادههاي مالتيمديا، از اين گراف استفاده ميكنند. دادههاي چند رسانهاي در اين گراف (در حالي كه كارها به وسيله برنامه كاربردي كنترل ميشوند) از فايل منبع به سمت مقصد كه ميتواند يك قطعه سختافزاري باشد حركت ميكنند. ولي در برخي مواقع، برنامه كاربردي علاوه بر كنترل گراف، دريافتكننده يا فرستنده داده نيز هست. هر گره اين گراف، همانگونه كه گفته شد، يك فيلتر است و كار ويژه خود را انجام ميدهد. فيلتر source، دادهها را از يك فايل يا URL ميخواند. فيلتر Parser، بخشهايي از دادههاي صوتي و ويديويي را به رمزگشاي مناسب ميفرستد. رمزگشاها، دادههاي صوتي و ويديويي را رمزگشايي مينمايند يا از حالت فشردگي خارج ميكنند. فيلتر رندركننده، دادههاي دريافت شده صوتي و ويديويي از رمزگشا را پخش ميكند يا آنها را نمايش ميدهد. 4- DirectSound اين كامپوننت همزمان با ساخت ويندوز 95، زماني كه درايورهاي صوتي از نوع VXD بودند به DirectX افزوده شد. در اين كامپوننت APIهاي ويژهاي ايجاد شد كه نويسندگان درايورهاي صوتي ميبايست آنها را به محصولات خود، كه فرمت VXD داشت، ميافزودند تا به درستي با DirectSound كار كند. برنامههاي چندرسانهاي با اين كامپوننت به سختافزارهاي صوتي مانند كارت صوتي دسترسي پيداميكنند. از مهمترين ويژگيهاي اين API، تركيب صدا و كنترل سطح آن است. DirectSound همچنين اجازه ميدهد چندين برنامه كاربردي، بدون پيش آوردن وقفه، همزمان به كارت صوتي دسترسي داشته باشند. ايجاد افكتهاي صوتي از ديگر تواناييهاي DirectSound است. پس از سالها توسعه، اكنون DirectSound يك API پخته و كامل است و بسياري قابليتهاي ديگر را نيز فراهم ميكند؛ مانند قابليت پخش صداهاي چند كاناله با وضوح و دقت بالا. 5- DirectMusic تاكنون بازيهايي را تجربه كردهايد كه در تمام مدت يك مرحله، موسيقي يكنواخت و ثابتي دارند؟ بازياي را در نظر بگيريد كه برنامهنويسان آن ميخواهند يك آهنگ، در تمام مدت، در يك مرحله از آن به صدا دربيايد. با استفاده از برنامه DirectMusic Producer، آنها ميتوانند در آن مرحله براي آهنگ، يك درجه در نظر بگيرند. اين درجه ميتواند بسته به نوع عملكرد شخصيت بازي، تغيير كند. اگر شخصيت بازي در حال راه رفتن است، آهنگ آرام و هنگامي كه با دشمن خود مبارزه ميكند، آهنگ تندتر ميشود و يا نوع آهنگ تغيير ميكند و هنگامي كه مبارزه تمام ميشود، آهنگ دوباره آرام ميشود. اين تغييرها بدون ايجاد وقفه، به صورت پويا و بدون دخالت كاربر انجام ميشود. چون براساس DirecMusic، آهنگ به صورت شناور و بدون وقفه با نواختن وارياسيونهاي مختلف با قابليت واكنش به رويدادهاي بازي توليد ميشود. DirectMusic، با دادههاي موسيقي براساس پيامهاي حاوي اطلاعات كار ميكند. يك آهنگ ميتواند در داخل سختافزار و با نرمافزارهاي آهنگساز مانند Microsoft Synthesizer ساخته شود. DirectMusic از استانداردهايMIDI و DLS پشتيباني ميكند. 6- DirectInput اين كامپوننت، سازوكار مشتركي را براي دسترسي به بسياري از كنترلكنندههاي بازي مانند دسته بازي، گيمپد، صفحه كليد و ماوس فراهم ميآورد. مهمترين تغييري كه هنگام عرضه DirectX8 در DirectInput ايجاد شد، آمدنaction map بود. action map از توابعي مانند راندن يك وسيله يا شليك يك گلوله (كه بهوسيله دستگاههاي ورودي ايجاد ميشود) استفاده ميكند. زماني كه يك سختافزار ورودي مانند دسته بازي را ميخريد، معمولا ًaction mapنيز براي بسياري از انواع رايج بازيها مانند شبيهساز پرواز، تيراندازي اول شخص و بازيهاي مسابقهاي در آن پيادهسازي شده است. 7- DirectPlay اين كامپوننت امكان بازي چند نفر را در بازيهاي چندنفره فراهم ميآورد، دسترسي به سرويسهاي ارتباطي را آسان ميسازد و راهي را براي بازيها فراهم ميكند تا مستقل از پروتكل يا نوع سرويس آنلاين با يكديگر در ارتباط باشند. همچنين از پروتكلهاي ارتباطي مطمئن پشتيبانيميكند تا مانع از گم شدن دادههاي مهم بازي روي شبكه شود. در واقع DirectPlay به صورت لايهاي است كه روي پروتكلهاي معمول شبكه مانند IPX ،TCP/IP و ... قرار دارد. در واقع يك session يا جلسه در DirectPlay يك كانال ارتباطي بين چندين كامپيوتر است. يك برنامه كاربردي پيش از آنكه بتواند با سيستمهاي ديگر ارتباط برقرار كند، بايد در يك Session يا جلسه باشد. هر جلسه تنها يك ميزبان دارد و آن برنامه كاربردياي است كه آن جلسه را ايجاد كردهاست. تنها ميزبان ميتواند ويژگيهاي يك Session را تغيير دهد. DirectX 9.0 اين كامپوننت، آخرين نسخه DirectX تا پيش از عرضه رسمي ويندوز ويستا است. مهمترين چيزي كه همراه DirectX 9.0 عرضه شد، High-Level Shader Language) HLSL) است. زبان HLSL جايگزين زبان اسمبلي براي نوشتن pixel shaderها و vertex shaderها در DirectX است. پيش از ارائه DirectX 9.0 توسعهدهندگان بازي بايدshaderها را با استفاده از يك زبان اسمبلي سطح پايين توسعه ميدادند. HLSL با فراهمآوردن يك محيط برنامهنويسي توسعهدهنده ساده، توسعه همه بخشهاي نرمافزار مانند انيميشن و برنامهنويسي افكتها را آسان ميكند. HLSL با همه پردازشگرهاي گرافيكي (GPU) سازگار با DirectX كار ميكند و به توسعهدهندگان امكان ميدهد افكتهاي بصري را روي گستره وسيعتري از پلتفرمها ايجاد كنند؛ بدون اينكه نياز داشته باشند به جزئيات سختافزار گرافيكي توجه كنند. DirectX 9.0 روي ويندوز 95 نصب نميشود. چون بازيهايي كه به DirectX 9.0 نياز دارند، به كامپيوترهاي جديدتر و قويتري هم نياز دارند كه ويندوز 98 يا نسخههاي جديدتر روي آنها نصب ميشود. تاكنون نسخههاي a ،b و c از DirectX 9.0 ارائه شده است. هر نسخه جديدتر از DirectX داراي امنيت، كارايي و سيستم رفع خطاي بهتري است. DirectX 10 دوستداران بازي بايد خوشحال باشند از اينكه بدانند شركت مايكروسافت DirectX را نيز توليد كرده است و همراه پيش توزيع Direct3D 10 عرضه خواهد شد. همچنين نرمافزارMicrosoft Windows Game Explorer نيز عرضه شده كه به برنامهنويسان و توسعهدهندگان امكان ميدهد امكانات بروزكردن خودكار (auto-updating) را به بازيهايشان بيفزايند. مايكروسافت ميخواهد DirectX 9.0 و DirectX 10 را روي ويندوز ويستا عرضه كند. به گفته Rodolph Balaz از برنامهنويسان توسعهدهنده Direct3D و OpenGL در مايكروسافت، DirectX 10 تنها با سيستمعاملهاي جديد كار خواهد كرد و در حال حاضر مايكروسافت، برنامهاي براي پشتيباني ويندوز اكسپي از آن ندارد. تا زمان نوشته شدن اين مقاله هنوز نسخه رسمي ويندوز ويستا عرضه نشده است. ولي به نظر ميآيد اين ويندوز، هم از DirectX 10 و هم از DirectX 9.0 پشتيباني خواهد كرد. SGL OpenGL شركت سيليكون گرافيكس(SGI ،OpenGL) را با هدف ساخت يك API براي توسعه برنامههاي گرافيكي دوبعدي و سه بعدي عرضهكردهاست. پيش از ساخته شدن APIهاي گرافيكي مانند OpenGL و DirectX، بسياري از توليدكنندگان سختافزار، كتابخانههاي گرافيكي مختلف و متفاوتي داشتند. به همين دليل پشتيباني از نسخههاي مختلف نرمافزارهايشان روي پلتفرمهاي سختافزاري مختلف هزينهبر و انتقال يك برنامه كاربردي از يك پلتفرم سختافزاري به پلتفرم سختافزاري ديگر بسيار وقتگير و سخت بود. بنابراين SGI نمونه برنامهاي را توليد كرد كه توليدكنندگان سختافزار بايد از آن براي توسعه درايورهاي OpenGL در سختافزارهايشان استفاده كنند. اين برنامه به صورت اپنسورس ارائه شدهاست. ولي سازندگان اين سختافزارها ميتوانند قابليتهاي گوناگوني را برپايه OpenGL در سختافزارهايشان ايجاد كنند. تصميمگيري درباره ايجاد تغييرات در OpenGL را كنسرسيوم ARB اتخاذ ميكند. اين كنسرسيوم شامل اعضاي مهمي همچون اپل، اينتل، آيبيام، سان، ATI، دل، nVIDIA، سيليكونگرافيكس و3Dlabs است و از سوي شركتهاي معتبر ديگري مانند متراكس، S3 ،Xi و Quantum 3D حمايت ميشود. توسعهدهندگان نرمافزار براي استفاده از OpenGL در نرمافزارهايشان نيازي به اخذ مجوز ندارند. ولي توليدكنندگان سختافزار براي پيادهسازي سختافزاري OpenGL نيازمند اخذ مجوز از SGI هستند. OpenGL چيست؟ در اوايل پيدايش OpenGL، از اين API در كارهاي صنعتي، طراحي وسايل داخلي، مكانيكي و نيز در آناليزهاي علمي و آماري استفاده ميشد. در سال 1996، نويسندگان و توسعهدهندگان بازيهاي كامپيوتري از نسخه ويندوزي OpenGL براي ساخت بازيهاي كامپيوتري استفاده كردند. OpenGL براي پشتيباني از گستره وسيعي از تكنيكهاي رندركردن گرافيكي پيشرفته طراحي شده است كه ميتوان پارهاي از آنها را به اينصورت نام برد: نورپردازي: قابليت تحليل ميزان رنگ هنگام تابش مدلهاي متفاوت نور به يك سطح از يك يا چند منبع نور مختلف. سايهسازي نرم: قابليت تحليل افكتهاي سايه هنگام تابش نور به يك زاويه و ايجاد اختلاف نور خفيف در مقابل آن سطح (مانند نور كمي كه هنگام تابش آفتاب به يك صخره يخي در اطراف آن ايجاد ميشود). حركت محو ومدلسازي: توانايي تغيير مكان و اندازه پرسپكتيو يك شي در فضاي سه بعدي. مجموعه امكانات OpenGL شبيه Direct3D است. ولي API سطح پايينتر آن (نزديكتر به سطح سختافزار) باعث ميشود كنترل خوبي روي عناصر اصلي ايجاد صحنههاي سه بعدي مانند اطلاعات سهضلعيها كه سلولهاي تشكيلدهنده يك مدل سه بعدي هستند داشته باشد. دو سطح پشتيباني از شتابدهندگي سختافزاري براي OpenGL وجود دارد: installing client driver) ICDs) كه به نوردهي ايجاد تغيير و رستركردن (تبديل يك فريم سه بعدي چند ضلعي ذخيره شده درframe buffer به يك تصوير كامل با بافتها و نشانههاي عمق و نور) شتاب ميدهد و mini client server) MCs) كه از رستركردن پشتيباني ميكند. OpenGL 1.4 و OpenGL 1.5 بهترتيب در تابستان 2002 و 2003 معرفي شدند كه هر يك امكانات و كاربردهاي بيشتري از نسخههاي پيش از خود داشتند. بزرگترين آنها OpenGL Shading Language بود؛ زباني ويژه برنامهنويسي vertex-shader و pixel-shader كه در صورت نياز به OpenGL الصاق ميشد. OpenGLShading Language زباني شد كه به سرعت در سطح گستردهاي مورد پشتيباني يونيكس، ويندوز، لينوكس و ديگر سيستمعاملها براي توسعهدهنده گرافيكهاي تعاملي و برنامههاي كاربردي ترسيمي قرار گرفت. OpenGL 2.0 OpenGL 2.0 آخرين نسخه عرضه شده تا اوايل سال 2006 ميلادي است. OpenGL Shader Language همراه با اين نسخه عرضه شده و بر پايه استاندارد ANSYC طراحي شده است. برخي قابليتهاي تازه اين نسخه عبارتند از: - سايهزني قابل برنامهريزي بهوسيله OpenGL Shader Language و APIهاي آن. قدرت ايجاد Shader و برنامهنويسي اشيا، بخش ديگري از تغييرات ايجاد شده در اين نسخه است. - رندر چندگانه كه به shaderهاي قابل برنامهنويسي امكان ميدهد در بافرهاي خروجي چندگانه در يك گذر مقادير مختلفي بنويسند. - بافتهاي دو طرفه، با قابليت تعريف كاربرد آن بافت براي سطح جلو و پشت يك مدل اوليه كه كيفيت حجم سايه و كارايي الگويمهاي رندر هندسي اشياي سخت را ارتقا ميدهد. - Spriteهاي نقطه كه مختصات بافت يك نقطه را با مختصات بافت قرار داده شده در مقابل آن نقطه جابهجا ميكنند و رسم نقاط را در بافتهاي طراحي شده در كامپيوترهاي معمولي نيز ممكن ميسازند. - بافتهاي Non-power-of-two كه براي همه انواع بافت كاربرد دارد كه در نتيجه از بافتهاي چهارگوش پشتيباني مينمايد و درعمل حافظه كمتري اشغال ميكند. OpenAL OpenAL، يك API ديگر است كه براي ايجاد و مديريت صداهاي سه بعدي در بازيهاي كامپيوتري و ديگر انواع نرمافزارها به صورت يك پروژه مشترك ميان شركت Loki Software و Creative ساخته شده است. كتابخانه اين API مجموعهاي از صداهاي قابل حركت در فضاي سهبعدي را مدلسازي ميكند. عناصر اصلي OpenAL شامل يك شنونده، يك منبع و يك بافر است. ممكن است تعداد زيادي بافر وجودداشته باشد كه شامل دادههاي صوتي هستند. هر بافر ميتواند به يك يا چند منبع ضميمه شود. هميشه يك عنصر شنونده (براي محتواي صوتي) وجود دارد كه موقعيت مكاني منبع صوتي كه صداي آن شنيده ميشود را نشان ميدهد. OpenAL در موتورهاي گرافيكي Epic Games Unreal نيز براي ساخت افكتهاي صوتي به كار ميرود. OpenGL Performer OpenGL Performer، رابط برنامهنويسي قدرتمند و كاملي است كه توسعهدهندگان براي شبيهسازي بصري از آن استفاده ميكنند. ابزارهاي موجود در آن، توسعه برنامههاي شبيهسازي بصري، طراحي بر اساس شبيهسازي، واقعيت مجازي، نرمافزارهاي علمي، سرگرميهاي تعاملي، برنامههاي ويديويي و طراحي با كامپيوتر را آسان ميكند. اين رابط برنامهنويسي به برنامهنويسان امكان ميدهد از قابليتهاي سيستم به صورت بهينه استفاده كنند. آخرين نسخه اين نرمافزار OpenGL Performer 3.2 است. OpenGL Volumizer OpenGL Volumizer، يك API گرافيكي است كه در بخشهاي انرژي، توليد، داروسازي و تجارت كاربرد دارد. اين API براي انجام كارهاي تعاملي با كيفيت بالا و بصري نمودن و شبيهسازي يك محيط با استفاده از مجموعه بزرگي از دادههاي حجمي (دادههايي كه مختصات يك شي در فضاي سه بعدي را نشان ميدهند) طراحي شده است. براي نمونه در نرمافزارهاي پزشكي براي شبيهسازي وضعيت بخش خاصي از بدن، از اين نرمافزار استفاده ميشود. OpenGL Volumizer آخرين نسخه اين API تا اوايل سال 2006 ميلادي است كه بر پايه كتابخانه گرافيكي استانداردOpenGL ساخته شده و شامل رابط كلاس ++C و قابلاستفاده در سيستمعاملهاي ويندوز و لينوكس 32 بيتي و 64 بيتي است. OpenGL Multipipe SDK OpenGL Multipipe SDK يك لايه API است كه مديريت برنامههاي گرافيكي را در زير سيستمها و ساختارهاي گرافيكي چندگانه آسان ميكند. برنامههاي كاربردي نوشته شده برپايه اين API به نرمي و رواني، هم روي سيستمهاي روميزي تك پردازندهاي و هم روي سيستمهاي چند پردازندهاي با سيستمهاي گرافيكي قدرتمند اجرا ميشوند. نتيجهگيري همانگونه كه بيان شد ارتباط بين برنامهها و سختافزاري كه آنرا اجرا ميكند برعهده API است. سازندگان بزرگ نرمافزار و سختافزار API خاصي را براي برنامههاي مالتيمديا آماده كردهاند كه مطرح ترين آنها DirectX و OpenGL هستند. برنامه های DirectX در دو حالت اجرا میشند: 1-حالت تمام صفحه (Full Screen Mode) 2-حالت پنجره ای (Windowed Mode) برنامه امروز در حالت Full Screen اجرا می شود.VB رو اجرا کنید و یه پروژه نوع استاندارد ایجاد کنید. برای این که بتوانیم از توابع اشیای DirectX استفاده کنیم باید کتابخانه Type Library رو به پروژه اضافه کنیم. برای این کار بر روی منوی Project کلیک و References رو انتخاب کنید. در پنجره باز شده و در لیست موجود DirectX 7.0 for Visual Basic Library Type را تیک بذارید و OK کنید. حالا برای اینکه ما بتوانیم از DirectX استفاده کنیم باید یک شیئ از نوع DirectX7 تعریف کنیم. پس در قسمت General فرم یک شیئ از این نوع تعریف کنید. Dim DX As New DirectX7 اشیاء دیگری که تعریف خواهیم کرد این ها هستند: 1-یک شیئ از نوع DirectDraw7 این همون شیئی که به ما کمک میکنه که سطوح رو ایجاد کنیم: Dim DD As DirectDraw7 2- 2 شیئ از نوع DirectDrawSurface7 این اشیاء سطوحی هستند که ما شکل ها، متون و ... رو بر روی اونها نگارش می کنیم(تخته سیاه) Dim Primary As DirectDrawSurface7 ‘سطح اصلی Dim Backbuffer As DirectDrawSurface7 ‘ پشت صحنه یک متغیر عمومی از نوع Boolean هم تعریف می کنیم. این متغیر مشخص می کنه که تا چه زمانی برنامه باید اجرا بشه: Dim EndPro As Boolean حالا ادامه میدیم. طریقه کار به این صورت که اول ما شیئ DD رو مقداردهی میکنیم. یعنی در حقیقت به DirectX7 میگیم که شیئ DD رو برای ما ایجاد کنه. پس در ادامه (Form_Load) بنویسید: Set DD = DX.DirectDrawCreate ("") حالا باید به شیئ DD که از نوع DirectDraw7 هست بگیم که ما میخوایم از کدوم فرم برنامه استفاده کنیم. من فرض کردم که فرم برنامه Form1 هست.همچنین باید به کامپیوتر بفهمونیم که میخوایم برنامه تمام صفحه باشه یا نه. برای اینکار از متد SetCooperativeLevel شیئ DD استفاده می کنیم: DD.SetCooperativeLevel Form1.hWnd, DDSCL_FULLSCREEN Or DDSCL_EXCLUSIVE حالا باید رزولوشن صفحه رو تغییر بدیم. فرض کنیم که سیستم شما از حالت 640 * 480 پشتیبانی می کنه. برای اینکار از متد SetDisplayMode شیئ DD استفاده می کنیم: DD.SetDisplayMode 640, 480, 16, 0, DDSDM_DEFAULT آرگومان اول عرض، آرگومان دوم Height و آرگومان سوم عمق بیت حالت رو نشون میده.اون یکی ها هم ref و Mode هستند حالا میخوایم سطوح رو مقداردهی کنیم. کدهای زیر رو به پروژه خود بیفزایید: Dim ddsd As DDSURFACEDESC2 ddsd.lFlags = DDSD_BACKBUFFERCOUNT Or DDSD_CAPS ddsd.lBackBufferCount = 1 ddsd.ddscaps.lCaps = DDSCAPS_COMPLEX Or DDSCAPS_FLIP Or DDSCAPS_PRIMARYSURFACE Or DDSCAPS_VIDEOMEMORY برای اینکه بتونید یه سطح رو ایجاد کنید باید یه شیئ از نوع DDSURFACEDESC2 رو به ان وابسته کنیم. حالا سطح اصلی (Primary) خودمون رو ایجاد کنیم. این سطح همون سطحی است که کاربر میبینه. (اینکه چه جوری کامپایلر میفهمه که این همون سطح هست برمیگرده به خصوصیات شیئ ddsd از نوع DDSURFACEDESC که بالا مقداردهی کردیم و این شیئ مشخص میکنه که Primary تخته سیاه باشه). با استفاده از متد CreateSurface شیئ DD این سطح رو ایجاد می کنیم. آرگومان این تابع همون شیئ بالاییه) داریم: Set Primary = DD.CreateSurface(ddsd) حالا باید سطح BackBuffer رو مقداردهی کنیم. ولی شاید بپرسید ما که Primary رو داریم دیگه این BackBuffer برای چیه؟ باید بگم برای اینکه از پرپر زدن صفحه نمایش جلوگیری کنیم، ما بجای اینکه همه چیز رو مستقیما روی سطح Primary رسم کنیم، میایم و اول روی سطح (BackBuffer) که متصل به Primary هستش رسم می کنیم و صحنه آماده رو منتقل می کنیم به Primary (در حقیقت میتونیم بگیم که BackBuffer چرک نویس ما هست). پس حالا ما سطح BackBuffer رو متصل به سطح Primary ایجاد می کنیم. داریم: Dim ddscaps As DDSCAPS2 ddscaps.lCaps = DDSCAPS_BACKBUFFER Or DDSCAPS_VIDEOMEMORY Set Backbuffer = Primary.GetAttachedSurface(ddscaps) حالا همه چی آماده است. در ضمن فکر میکنم همه با نوع داده RECT آشنایی داشته باشند (اگه کسی آشنایی نداره در قسمت نظرات بگه تا من اونو هم توضیح بدم). در دایرکت ایکس برای اینکه بخوایم یه قسمتی از صفحه رو مشخص کنیم از این نوع داده استفاده می کنیم. یه متغیر هم از این نوع تعریف می کنیم تا کل صفحه رو مشخص کنیم. (برای رنگ آمیزی کل سطح و ...) توجه داشته باشید که این نوع داده در کتابخانه DirectX موجود هست و شما نیازی ندارید که مثل برنامه های دیگه این نوع رو تعریف کنید : Dim rec As RECT rec.Bottom = 480 rec.Left = 0 rec.Right = 640 rec.Top = 0 اینو به یاد داشته باشید که ما در برنامه هایی که با دارکت ایکس می نویسیم. کل عملیات رو در قالب یه حلقه (معمولا حلقه Do) انجام میدیم. به این صورت که پشت سر هم به طور مداوم صحنه های خودمون رو بر روی سطح ترسیم می کنیم و به Primary منتقل می کنیم. پس در پروژه خویشتن ( و در همان Form_Load، یعنی ادامه کدهای قبلی) بنویسید: Do While EndPro=False Backbuffer.BltColorFill rec, 0 ‘پاک کردن صفحه Backbuffer.SetForeColor RGB(256, 0, 0) ‘ تنظیم رنگ ترسیم Backbuffer.DrawText 300, 250, “Hello World”, False ‘درج متن DoEvents ‘توضیح در پایین Primary.Flip Nothing, DDFLIP_WAIT ‘ انتقال از چرک نویس به پاک نویس Loop حالا ببینیم که چیکار کردیم. تا زمانی که مقدار متغیر EndPro نادرست است این عملیات رو انجام میدیم: برای پاک کردن صفحه هر بار صفحه رو با رنگ سیاه پر میکنیم. متد BltColorFill سطح رو به روش بلیت پر میکنه. یعنی این که در حافظه مقادیر هر پیکسل رو برابر رنگی که بهش میدیم قرار میده(در اینجا این رنگ رو برابر 0 که همون سیاه هست قرار دادیم). با استفاده از متد SetForeColor رنگ تمام ترسیماتی که انجام میدیم رو عوض می کنیم.(به اصطلاح ForeColor سطح رو تغییر میدیم.) با استفاده از متد DrawText متن مورد نظرمون رو بر روی صفحه حک می کنیم. دو آرگومان اول مختصات x و y محل درج متن رو مشخص می کنند. مـــــــهـــــــم : وقتی ما پشت سر هم و بدون وقفه یه کاری رو انجام میدیم، برنامه دیگه چیز دیگه ای رو پردازش نمی کنه. بنابراین ما با این عبارت(DoEvents) به کامپایلر میگیم در هر بار انجام دستورات حلقه، دستورات دیگه ای مانند فشرده شدن کلید، کلیک ماوس و ... رو هم پردازش کنیم. اگه خیلی کنجکاو هستید، بعد از این که پروژه تون رو Save کردید یه بار برنامه رو بدون DoEvents اجرا کنید حالا صحنه ما آماده است پس اونو میذاریمش جلوی چشم کاربر. این کار با استفاده از متد Flip سطح Primary انجام میشه. (درمورد فلیپ بعدا اگه عمری باقی باشه توضیح میدم.) هنگامی که متغیر EndPro مقدار Trueبگیره(هنگامی که کاربر قصد خروج دارد)، حلقه شکسته میشه و ما باید برنامه رو به پایان ببریم. پس حافظه ای که به سطوح و اشیاء دیگر اختصاص داده ایم رو آزاد میکنیم: Set DD = Nothing Set Primary = Nothing Set Backbuffer = Nothing Set DX = Nothing End در اینجا Form_Load به پایان میرسه. حالا فقط یه کار مونده. اون هم اینه کا وقتی کاربر کلید Esc رو فشار میده باید برنامه به پایان برسه، یعنی مقدار متغیر EndPro برابر True بشه. پس این خطوط کد رو به پروژه تون اضافه کنید: Private Sub Form_KeyPress(KeyAscii As Integer) If KeyAscii = 27 Then EndPro=True End Sub حالا برنامه رو اجرا کنید تمرین: 1-برنامه را طوری تغییر دهید که به جای عبارت Hello World نام خودتان را نمایش دهد. 2-برنامه را طوری تغییر دهید که به جای عبارت Hello World ، زمان سیستم را نمایش دهد. 3-کاری کنید که عبارت Hello World در روی صفحه به سمت چپ (یا راست حرکت کند) DirectX 10 و اهمیت آن در صنعت کامپیوتر های شخصی توضیح : این گفتگو در تاریخ 19 مهرماه در سایت BootDaily منتشر شده است. در این مصاحبه پرسش ها توسط BD (کوتاه شده عبارت Boot Daily) و پاسخ ها با نام Chris (مدیر بخش گسترش و بازاریابی مایکروسافت) مشخص شده اند. همچنین منظور از DX10 در این مصاحبه DirectX 10 می باشد. BD : لطفا اهمیت DX10 برای دوست داران بازی ها را در یک جمله توضیح دهید. Chris : ما در مایکروسافت DirectX 10 را به عنوان بلند ترین جهش در کیفیت و کارایی گرافیک در صنعت کامپیوتر های شخصی از زمان پیدایش DirectX که به زمان ویندوز 95، و تحولی در زمینه بازی های کامپیوتری و تکنولوژی مربوط به آن می دانیم. BD : و اهمیت آن برای کاربران عادی؟ Chris : بله، DirectX 10 بالاترین کیفیت و کارایی گرافیکی را در پلاتفرم ویندوز را به همراه خواهد داشت که نتیجه آن تجربه ای باورنکردنی و دور از ذهن برای کاربران عادی خواهد بود. BD : این یک حقیقت است که تعداد زیادی از بازی های معروف و پرطرفدار درحال حاظر بر پایه موتور گرافیکی Doom3 که از OpenGL بهره می گیرد ساخته می شوند. به نظر شما DX10 چه مزیت هایی نسبت به OpenGL برای تولید کنندگان بازی های کامپیوتری به ارمغان می آورد؟ Chris : همانطور که می دانید، ما DirectX 10 را برای ویندوز ویستا و با هدف ارایه دادن یک تجربه باورنکردنی از جزئیات گرافیکی به کاربران و بازیکنان ، تماما از پایه و از صفر ساخته ایم. این نسخه از DirectX نسب به نسخه های قبلی دارای بازده به مراتب بیشتر و در عین حال با ضریب اطمینان و پایداری بیشتر خواهد بود. DirectX 10 به لطف Shader Model 4.0 به طور فوق العاده ای کیفیت و جزئیات باورنکردنی را در اختیار توسعه دهندگان و نهایتا کاربران خواهد گذاشت که از آن جمله توانایی بکارگیری محیط های پیچیده گرافیکی و ترسیم چهره ها به صورت بسیار طبیعی و توانایی نمایش تعداد دلخواه از آیتم ها در صحنه می باشد. اشاره کردید که بازی های معروف و پرطرفدار بر اساس موتور گرافیکی Doom3 و OpenGL ساخته می شوند، اما به عقیده من تعداد بازی هایی که از DirectX استفاده می کنند به مراتب بیشتر و حتی زیبا تر هستند. بازی هایی زیادی هستند که بر اساس موتور های گرافیکی Unreal و Source و با استفاده از DirectX ساخته می شوند. حتی ID (منظور شرکت ID Software سازنده سری بازی های Doom و Quake است-م) نیز برای منطبق ساختن بازی های خود با کنسول های Xbox و Xbox360 آنها را به DirectX تبدیل می کند. با این حال، تمامی برنامه هایی که از واسط گرافیکی OpenGL استفاده می کنند قابلیت اجرا در ویندوز ویستا را به شرط پشتیبانی درایور گرافیک و استفاده از وصله (Patch) مناسب، خواهند داشت. به همین خاطر سازندگان و فروشندگان قطعات سخت افزاری ملزم به ارایه ICD یا Installable Client Drivers که امکان استفاده از شتابدهنده گرافیکی را برای پردازش دستورات OpenGL را خواهد داد، هستند BD : چرا این نسخه از DX10 تنها قابل استفاده در ویندوز ویستا است؟ آیا این نوعی اجبار کاربران برای خرید و ارتقا به ویندوز ویستا نیست؟ DirectX 10 : Chris نیاز به امکانات مشخصی جهت بهره گیری کامل از سخت افزار گرافیکی سیستم دارد که این امکانات و خصیصه ها تنها در سیستم عامل ویندوز ویستا موجود است. ما یک هدف بزرگ را برای دراختیار گذاشتن تجربه گرافیکی جدید به کاربران در نظر گرفته ایم و همانطور که قبلا گفتم، DirectX 10 کاملا از پایه و بدون استفاده از نسخه های قبلی این واسط گرافیکی ساخته شده است و بنابر این هیچگونه سازگاری با نسخه های قبلی از سیستم عامل ویندوز ندارد. DirectX 10 برپایه مدل جدید درایور تصویر ویندوز ویستا (Windows Vista Display Driver Model یا WDDM) که معرف عصر جدیدی در قابلیت های گرافیکی و افزایش پایداری و ضریب اطمینان درنظر گرفته شده، طراحی شده است. در عین حال ما با انجام تغییراتی در معماری مدل درایور ها سعی در آسان سازی و افزایش پایداری و همچنین سازگاری بیشتر تراشه های گرافیکی با مجموعه دستورات هوش مصنوعی و محاسبات فیزیکی (جدا از تراشه های محاسب فیزیکی) انجام داده ایم که مجموعه این دلایل برای ارایه نشدن این نسخه از DirectX جهت سیستم عامل های قبلی مایکروسافت از جمله ویندوز XP متقاعد کننده به نظر می رسد. BD : به غیر از رشته های کوتاه تر (Shorter Program Strings)، مزیت اصلی Shader Model 4.0 نسبت به نسخه 3.0 آن برای توسعه دهندگان بازی های کامپیوتری چست؟ Shader Model 4.0 : Chris به توسعه دهندکان اینگونه نرم افزار ها اجازه انجام محاسبات پیچیده بیشتری را در تراشه های گرافیکی (GPU) می دهد. این عمل ضمن کاهش بار پردازش از پردازنده سیستم را که باعث عدم بهره گیری از قدرت کامل تراشه گرافیکی می شود، اجازه انجام محاسبات سنگین مربوط به هوش مصنوعی و نیز افزایش تعداد آیتم ها را در صحنه در اختیار توسعه دهندگان قرار خواهد داد. در عین حال DirectX 10 به همراه Shader Model 4.0 به لطف پشتیبانی از سایه زن های متحد (Unifyed Shaders) انعطاف پذیری و خلاقیت بسیار بیشتری را در اینگونه توسعه دهندگان قرار خواهد داد. BD : شرکت های ATI و nVIDIA هردو درحال طراحی و ساخت تراشه های گرافیکی سازگار با DX10 هستند. به نظر شما این تراشه های گرافیکی در مقایسه با کنسول های بازی Xbox 360 و PS3 چگونه عمل خواهد کرد؟ Chris : این موضوع و تلااش این دو شرکت در مورد DirectX 10 بسیار هیجان انگیز است. اما بهتر است این سوال را از خود آنها بپرسید! BD : به غیر از برتری های DX10 در مورد بازی های کامپیوتری، این نسخه چه نقشی را در برنامه های کاربردی آینده بازی خواهد نمود؟ Chriss : کلا DirectX فراتر از صرفا یک واسط و مجموعه دستورها برای بازی های کامپیوتری است. در گذشته نیز بسیاری از برنامه های کاربردی از CAD/CAM و 3DStudio گرفته تا برنامه های پزشکی و تصویر برداری نیز از مزایای DirectX بهره گرفته اند. با این حال DirectX 10 باز هم نسبت به نسخه های قبلی قطعا امکانات بسیار مناسب تری را در اختیار اینگونه برنامه ها خواهد گذاشت. به یک نکته دقت کنید! ویندوز ویستا از DirectX 10 به عنوان یک مولفه گرافیکی استفاده می کند - پس ویندوز ویستا هم خود یک برنامه کاربردی استفاده کننده از DirectX 10 محسوب می شود! BD : نحوه تعامل DX10 با کاربرانی که از سخت افزار DX10 استفاده نمی کنند چگونه خواهد بود؟ DirectX 10 : Chriss و ویندوز ویستا کاملا با تمامی برنامه ها و بازی های منطبق با نسخه های قدیمی تر DirectX سازگار هستند. کسانی که در زمان انتشار ویندوز ویستا، سخت افزار منطبق بر DirectX 10 را در اختیار ندارند و یا تهیه نکرده اند، هنوز هم قابلیت اجرای بازی ها و برنامه های برپایه DirectX 10 را خواهند داشت. DirecrX 9 هم در ویندوز ویستا برای این گروه از کاربران در نظر گرفته شده است. با این حال جالب است بدانید کاربرانی که با استفاده از سخت افزار منطبق بر DirectX 10 بازی های DirectX 9 را اجرا کنند شاهد افزایش کیفیت و بازده آن خواهند بود! درعین حال توسعه دهندگان نیز می توانند با خیالی آسوده به تولید بازی ها و نرم افزار های بر اساس DirectX 9 برای ویندوز ویستا بپردازند. BD : آیا به نظر شما DX10 نهایت گرافیک در کامپیوتر خواهد بود؟ یعنی با فرض در اختیار داشتن سخت افزار مناسب آیا شاهد تصاویر منطبق بر واقعیت با سرعت نمایش مناسب خواهیم بود؟ Chris : ما به DirectX 10 به عنوان گام بلندی در زمینه گرافیک کامپیوتری می نگریم. مطمنا این نسخه از DirectX تجربه جدید را در اختیار کاربران خواهد گذاشت و با اجازه دادن به توسعه دهندگان برای آشنایی با قابلیت های DirectX 10 این تجارب بهتر و بهتر نیز خواهند شد. اما ما همچنین عقیده داریم که هنوز هم موارد زیادی در این مورد قابل دستیابی هستند و بدست آوردن آنها نیز به زمان بیشتری نیاز دارد. گرافیک در ویندوز و DirectX 10 هنوز هم در راه توسعه و تکامل هستند. BD : آیا شما در این لحظه در مورد تراشه های DirectX 10 شرکت های ATI و nVIDIA نظری دارید؟ کدام یک را برای خود انتخاب می کنید؟ Chris : نه! ما بدون همکاران خود در جای فعلی قرار نداشتیم! مایکروسافت با تمامی شرکت های فعال در زمینه گرافیک کامپیوتری همکاری نزدیک و دوستانه ای داشته و دارد. هردوی آنها همکاری های بسیاری در توسعه پلاتفرم ویندوز تا کنون با ما داشته اند. دیدن محصولات آنها برای ویندوز ویتا در فروشگاه های سخت افزاری برای ما جال خواهد بود. BD : در مورد پیشرفت های صدا در DX10 نیز توضیح دهید. آیا ما میتوانیم با این نسخه صداها را "ببینیم" !؟ Chris : این نسخه از DirectX کماکان روال گذشته را در مورد چگونگی و نحوه تولید صدا ها ( بر پایه XACT cross-platform audio creation tool) را ادامه می دهد. بنابر این برتری عمده ای نسبت به نسخه های قبلی در این نسخه قابل ذکر نیست اما پیشرفت هایی انجام شده است. BD : ویندوز ویستا چگونه برای اجرای یک بازی یا برنامه با استفاده از DX10 و یا DX9 را با توجه به نوع GPU تصمیم گیری می کند؟ Chris : وقتی روند اجرای یک بازی آغاز می شود، ویندوز ویستا با تشخیص نسخه DirectX به کار رفته در بازی و سخت افزار سیستم، گزینه مناسب را انتخاب خواهد کرد. BD : گفته می شود که DirectX10 از Geometry Shaders (سایه زن هندسی) استفاده می کند. این قابلیت تا چه حد به روند انجام و تشکیل اشکال هندسی کمک خواهد کرد؟ Geometry Shader : Chris دقیقا در بین سایه زن های پیکسل و راس (Vertex and Pixel) در خط لوله پردازش قرار دارد. می تواند از رئوس و مثلث های ایجاد شده در ترسیم اشکال بعدی، بدون نیاز به پردازش مجدد، استفاده کند. سایه زن هندسی کارهای دیگری نیز انجام میدهد : تقویت تعداد مثلث ها با انتخاب یک یا چند مثلث از قبل پردازش شده و تکرار آنها بدون نیاز به پردازش مجدد، و یا تشکیل مثلث های جدید با ترکیب مثلث های دردسترس، تولید نقطه ها و خط های جدید و یا با استفاده از مثلث های ازقبل پردازش شده، و یا تولید پیکسل های پخش شده (جدا جدا). انتخاب یک نقطه، تولید یک سری مثلث در اطراف آن و گسترش آن تا تشکیل یک تصویر قابل درک (Sprite) و یا تجزیه یک مثلث به تعداد کوچکتری مثلث و گسترش آن. بیرون انداختن یک راس از مثلث از مجموع مثلث ها و تبدیل آن به یک حجم یا یک چهار ضلعی. بهترین نقطه در DirectX ترکیب تمامی انواع Shader Model ها در Shader Model 4.0 است که به توسعه دهندگان اجازه خواهد داد انواع Vertex، Pixel و Geometry Shaders ها را در یک مدل بیافرینند. BD : در مورد محاسبات فیزیک (کنش ها و واکنش های محیط در مقابل تعییرات) چطور؟ Chris : ویندوز ویستا از تمامی انواع محاسبات فیزیکی چه به وسیله پردازنده، تراشه گرافیکی یا پردازنده فیزیک جداگانه پشتیبانی خواهد نمود. با اتکا بر DirectX 10 ویندوز ویستا پلاتفرم قدرتمندی برای انجام محاسبات فیزیکی توسط GPU خواهد بود. پشتیبانی از ویژگی چند تراشه گرافیکی SLI و یا CrossFire و توانایی تقسیم وظایف پردازش تصویر و یا محاسبات فیزیکی بین تراشه های گرافیکی موجود در سیستم از جمله امتیازات سیستم عامل ویستا و DirectX 10 می باشد. BD : با این وجود پشتیبانی از کارت پردازش فیزیک Ageia به عهده ویندوز ویستا خواهد بود؟ و یا ATI و nVIDIA؟ Chris : واضح است که ساده ترین روش پردازش فیزیک، بهره گیری از پردازنده اصلی سیستم است. در ویندوز ویستا تامین درایور های لازم و پشتیبانی تراشه های پردازش فیزیک (مانند Agiea) بر عهده خود سازندگان آنها خواهد بود. BD : آیا تیم توسعه و بازار یابی شما اقدامی در جهت موجود بودن محتوای مناسبی مانند بازی های کامیوتری، نرم افزار های سازگار و مختص به ویندوز VISTA در هنگام انتشار آن انجام داده است؟ Chris : بله! در زمان انتشار ما پشتیبانی کاملی از تمام نسخه های موجود بازی ها و نرم افزار ها را در ویندوز ویستا شاهد خواهیم بود. در عین حال علاوه بر 10 عنوانی که قبلا برای انتشار همزمان با ویندوز ویستا اعلام شده بود، عناوین مهم و جذاب دیگری نیز برای انتشار همزمان با ویندوز ویستا پیش بینی کرده ایم. مطمنا این عنواین در همان روزهای اول پیشرفت فوق العاده در نسل بعدی بازی های کامپبوتری و امکانات باورنکردنی ویندوز ویستا و DirectX 10 را به نمایش خواهند گذاشت. ضمنا تعداد تعداد زیادی از عنواین فعلی نیز برای ویندوز ویستا و پشتیبانی آن از پردازنده های چند هسته ای بهینه سازی شده اند. از عناوینی که در هنگام انتشار ویستا برای خرید موجود خواهد بود : “Alan Wake (Microsoft Game Studios) “Age of Conan: Hyborian Adventures” (Eidos) “Company of Heroes” (THQ) “Crysis” (EA-Partners) “Flight Simulator X” (Microsoft Game Studios) “Halo 2 for Windows Vista” (Microsoft Game Studios) “Hellgate: London” (Namco) “LEGO Star Wars II: The Original Trilogy” (LucasArts) “Shadowrun” (Microsoft Game Studios) “Zoo Tycoon 2: Marine Mania” (Microsoft Game Studios) BD : از اینکه به سوالات ما و تعداد زیادی از کاربران و دوستداران بازی های کامپیوتری پاسخ دادید متشکرم. به امید موفقیت هرچه بیشتر شما. ما مشتاقانه منتظر عرضه ویندوز ویستا و بازی های جذاب DirectX 10 و سخت افزار های ارایه شده توسط nVIDIA و ATI خواهیم بود. |
||
|
|
|
|
|
ترفنـــــــــــــــــــــــــــد: برای اینکه بعد از قرار دادن کنترل هاتون نخواید دوباره فونت تک تک اونها رو تغییر بدهید برای این کار قبل از قرار دادن اونها روی فرم ابتدا فونت فرم رو به فونت مورد نظرتون که من Tahoma رو پیشنهاد میکنم تغییر بدهید و سپس هر کنترلی که روی فرم قرار بدهید با فونت فرم یکی میشود نکتـــــــــــــــــــــــــــــه: برای حرکت دادن کنترل های رو فرم به صورت دقیق تر و بدون لرزش میتونید این کار رو با کیبرد انجام بدهید که فقط روی کنترل مورد نظر کلیک کنید و سپس کلید کنترل رو پایین نگه دارید و با کلید های مکان نما اون رو جابجا کنید که این کار رو می تونید با انتخاب دسته جمعی کنترل ها هم انجام بدهید نکتـــــــــــــــــــــــــــــه: برای تغییر اندازه کنترل های روی فرم میتونید ابتدا انها را انتخاب کنید و سپس کلید شیفت رو پایین نگه دارید و با کلید های مکان نما اونها رو تغییر اندازه بدهید نکتـــــــــــــــــــــــــــــه: بعضی مواقع شما نوشته داخل یک کنترل مثل یک Label رو به فارسی تغییر میدهید که این عبارت به درستی نشان داده نخواهد شد که برای رفع این مشکل باید فونت اون کنترل رو به Tahoma تغییر دهید نکتـــــــــــــــــــــــــــــه: برای اینکه در پنجره خصوصیت اشیاء سریعتر به خصوصیت مورد نظر پرش کنید میتوانید با نگه داشتن کلید کنترل و شیفت و زدن اولین حرف خصوصیت مورد نظر به ان پرش کنید برنامه نمونه مشاور املاک(درخواستی) کدهای اماده برای کار های مختلف بخش اول ویژوال بیسیک شبیه سازی شده(انگلیسی) بخش دوم بخش سوم
فارسی ساز ویژوال بیسیک ( در فایل متنی داخلش توضیح دادم ) بخش اول پسوردش هم : VBLog.blogfa است حتمــــــــــــــــــــــــا دانلود کنید هر بخش ۱۵۰ کیلو بایت است بخش دوم بخش سوم بخش چهارم بخش پنجم بخش ششم بخش هفتم بخش هشتم بخش نهم
پنجره شناور (اقا کمال) قسمتی از وی بی اکواریــــــــــــــــــــــــــــــــم جالبــــــــــــــــــــــــــــــــه توابــــــــــــــــــــــــــع ماتریکس برای بچه های مهندسی و ریاضی طیف نما برای کارهای مولتی مدیا جالب بک گراند Fifa برای جام جهانی 2010 خودم طراحی کردم یک Progress bar کاربردی و جالب باز کردن Combo box با زدن اینتر متوقف کردن برنامه در تاریخ معین شده تبدیل فوریه برای بچه های الکترونیک و ریاضی یک نمونه کوچک از برنامه خودم(وی بی فارسی)(اقا کمال) اموزش برنامه نویسی Socket Programing یک نمونه کوچک از برنامه خودم (نوار مرزی) شبیه سازی یک قسمت از Nero ( نمایش طیفی فایل صوتی) اینها ادامه دارند سر بزنید پاسخ اقا مهدی عزیز: من اون راه حل های که فکر میکنم جواب بده رو مینویسم - 1 ابتدا وارد Run ویندوز شو و عبارت regsvr32 Mswinsck.ocx رو تایپ کن که این کار برای ثبت یک کامپوننت است و ببین مشکل رفع شد- 2 این فایل رو از پایین دانلود کن و در پوشه System32 ویندوز کپی کن و هنگام کپی yes رو بزن و مرحله اول رو مجددا انجام بده- 3 اگه باز هم مشکل داشتی فکر کنم مشکل از خود وی بی است پس دوباره وی بی رو نصب کنبرای درگ یک فرم به وسیله یک کنترل : یک دکمه یا کامند باتون رو فرم قرار بدید و این کد ها رو تو فرم کپی کنید Option Explicit Private Declare Function ReleaseCapture Lib "user32" () As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Sub Command1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single )Call ReleaseCapture Call SendMessage(hWnd, &HA1, 2, 0 &)End Sub بچه ها از اگه در اخرین ژست عکس پروژه من (وی بی فارسی) نبود برید تو ارشیو و ببینیدش و به عظمت کار پی ببرید راستی شبیه سازی پاور دی وی دی ۶ هم یادم نرفته دارم روش کار میکنم ( خیلی جالبه)
|
||
|
|
|
|
|
مطالب درخواستی مجبور کردن Common Dialog به ذخیره کردن فایل ها در یک درایو خاص ابتدا یک فرم خالی درست کنید و یک کنترل Common Dialog روی ان قرار دهید و سپس یک Command Button کد زیر را در رویداد کلیک کامند باتون قرار دهید Private Sub Command1_Click() Call Save_driveA End Sub یک TextBox روی فرم قرار دهید بعد تابع جدیدی به نام Save_DriveA مثل زیر درست کنید Function Save_DriveA() Dim x As Integer Do commondialog1.Action = 2 ' save file If UCase(Left(commondialog1.FileName, 1)) <> "A" Then MsgBox "YOu Must Save File to Drive A only" Else Exit Do End If DoEvents Loop x = FreeFile Open commondialog1.FileName For Output As #x Print #x, Text1.Text Close #x MsgBox "File has been saved to A" End Function ِDoEvents چیست؟ وقتي ما پشت سر هم و بدون وقفه يه کاري رو انجام ميديم برنامه ديگه چيز ديگه اي رو پردازش نمي کنه. بنابراين ما با اين عبارت(DoEvents) به کامپايلر ميگيم در هر بار انجام دستورات حلقه، دستورات ديگه اي مانند فشرده شدن کليد، کليک ماوس و ... رو هم پردازش کنيم.برای امتحانش هم یک برنامه بنویسید که داخلش یک حلقه باشد و یک بار بدون DoEvents اجراش کنید و بار دیگر در یک خط جدید عبارت DoEvents رو تایپ کنید و بعد اجراش کنید. Randomize Timer چیست؟ فرض کنید که شما میخواهید یک برنامه بنویسید که با هر بار اجرا شدن یا زدن یک دکمه یک عدد تصادفی را خودش انتخاب کند و ادامه ماجرا. خوب حالا با هر بار اجرای برنامه یا زدن دکمه همون اعداد تصادفی که در سری های قبلی ایجاد شده بود تولید میشوند که با قرار دادن عبارت Randomize Timer در ابتدای کدهای برنامه دیگر این مشکل نخواهد بود چون اون عدد تصادفی با ساعت داخلی ماشین انتخاب خواهد شد. ترفند شما میتوانید با زدن همزمان کلیدهای کنترل و Space یک منوی فوری در قسمت کد وی بی اجرا کنید که برای ساده کردن کد نویسی کاربرد دارد مثلا شما یک فرم به نام frmoption دارید که میتوانید در قسمت کد ویندو فقط عبارت frmop را بنویسید و سپس کلیدهای کنترل و space را بزنید که این پنجره عبارت شما را تشخیص خواهد داد و ان را تکمیل خواهد کرد که شما با تمرین میتوانید از ان در جاهای دیگر هم استفاده کنید کاربرد دیگر : وقتی وارد کد ویندو میشوید این کلیدها را بفشارید و میبینید که تمام توابع داخلی و ... ویژوال بیسیک نمایش داده خواهد شد که Doevents هم میتوان با این روش در بین کدها قرار داد که این هم یکی از توابع داخلی ویژوال بیسیک است موفق باشید یک ترفند فوق العاده فوق العاده کاربــــــــــــــــــــردی این ترفند رو خودم کشف کردم و در اختیار دوستان خوبم میگذارم با این ترفند خود ویژوال بیسیک 6 به سبک Xp در میاید (دقت کنید گفتم ویژوال بیسیک نه برنامه هاش) ابتدا یک فایل متنی از نوع Text documents در دسکتاپ خود درست کنید و این کدها را در داخل ان کپی کنید <? xml version="1.0" encoding="UTF-8" standalone="yes"?>< assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">< assemblyIdentity type="win32" processorArchitecture="*" version="6.0.0.0" name="mash"/>< description>Enter your Description Here</description>< dependency>< dependentAssembly>< assemblyIdentitytype="win32 "name="Microsoft.Windows.Common-Controls" version="6.0.0.0 "language ="*"processorArchitecture ="*"publicKeyToken="6595b64144ccf1df "/> </ dependentAssembly></ dependency></ assembly>
|
||
|
|
|
|
|
موضوع : پخش افکتهاي صوتی در برنامه هاي مالتي مديا ساخت بافر و play کردن آن : تاکنون ما توانستيم DirectSound را initial کنيم . همانطور که می دانيد در تمام component های DirectX داده ها در يکسری بافر ذخيره می شوند . در مورد DirectSound نيز ما يک بافر با نام DirectSoundSecondaryBuffer8 می سازيم و داده های صوتی را در آن قرار می دهيم . برخی پارامتر ها هستند که بايد برای بافر تنظيم شوند مثل : stereo يا mono بودن بافر ، ۸ بيتی يا ۱۶ بيتی بودن بافر ، فرکانس صوتی ( 22khz ، 44khz و غيره ) . اگر اين پارامترها را مشخص نکنيم DirectSound از اطلاعات فايل صوتی استفاده می کند . در يک کاربرد ساده ، ما تنها يک بافر صوتی از يک فايل ايجاد می کنيم اما امکان ايجاد چندين بافر بطور همزمان و نيز پخش چندين صدا بطور همزمان نيز وجود دارد : DSBDesc.lFlags = DSBCAPS_CTRLFREQUENCY Or DSBCAPS_CTRLPAN Or DSBCAPS_CTRLVOLUME Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\Sample.wav", DSBDesc)x MsgBox "SOUND BUFFER CREATED:"x MsgBox "Buffer Size: " & DSBDesc.lBufferBytes & "bytes (" & Round(DSBDesc.lBufferBytes / 1024, 3) & "kb)"x MsgBox "Buffer Channel Count:" & DSBDesc.fxFormat.nChannelsIIf(DSBDesc.fxFormat.nChannels = 1, " (Mono)", " (Stereo)")x MsgBox "Buffer Bits per channel: " & DSBDesc.fxFormat.nBitsPerSample & " bits"x در بالا يک بافر صوتی ايجاد شده و اطلاعات صدا از فايل به بافر load شده است . حال بايستی داده صوتی موجود در بافر را play کنيم : دستور لازم برای Play کردن بافر بصورت loop : DSBuffer.Play DSBPLAY_LOOPING دستور لازم برای Play کردن بافر بدون loop : DSBuffer.Play DSBPLAY_DEFAULT دستورات لازم برای Stop کردن بافر : DSBuffer.Stop DSBuffer.SetCurrentPosition 0 دستور لازم برای Pause کردن بافر : DSBuffer.Stop تنظيم خصوصيات بافر : سه خصوصيت وجود دارد که در مورد بافر تنظيم می شود pannig ، volume و frequency محدوده مقادير pannig بين اعداد زير است : DSBPAN_LEFT = -10,000 DSBPAN_CENTER = 0 DSBPAN_RIGHT = 10,000 توسط متد SetPan می توان pannig بافر را تنظيم کرد : DSBuffer.SetPan yourValue DirectSound صدا را تقويت نمی کند بلکه آنرا تضعيف می نمايد بنابراين ماکزيمم volume عبارت است از volume ای که فايل صوتی با آن ضبط شده است . بعبارت ديگر محدود مقادير volume بين اعداد زير است : DSBVOLUME_MAX = 0 DSBVOLUME_MIN = -10000 توسط متد SetVolume می توان volume بافر را تنظيم کرد : DSBuffer.SetVolume yourValue محدود فرکانسی DirectSound عبارت است از : DSBFREQUENCY_MIN = 100 (hz)x DSBFREQUENCY_MAX = 100000 (hz) = 100khz x توسط متد SetFrequency می توان فرکانس بافر را تنظيم کرد : DSBuffer.SetFrequency yourValue موضوع : پخش موزيک توسط DirectMusic مقدمه : در اولين درس از آموزش DirectXAudio با چگونگي پخش افکتهاي صوتي آشنا شديد . اکنون اين توانايي را داريد که يک engine ساده صوتي بنويسيد . در اين بخش مباني پخش موزيک را فرا خواهيد گرفت . پس از اين درس شما مي توانيد يک ماژوال براي پخش موزيکهاي پس زمينه و افکتهاي صوتي براي برنامه هايتان ايجاد کنيد . Initil کردن DirectMusic8 : قبل از هر کار بايستي ماژول DirectMusic8 را مقداردهي اوليه کنيد . اينکار بصورت زير انجام مي شود : Option ExplicitImplements DirectXEvent8 Private oDX As DirectX8 Private oDMPerf As DirectMusicPerformance8 Private oDMLoader As DirectMusicLoader8 Private oDMSeg As DirectMusicSegment8 Dim dmParams As DMUS_AUDIOPARAMS Set oDX = New DirectX8 Set oDMPerf = oDX.DirectMusicPerformanceCreate Set oDMLoader = oDX.DirectMusicLoaderCreate oDMPerf.InitAudio frmMain.hWnd, DMUS_AUDIOF_ALL, dmParams, Nothing, DMUS_APATH_DYNAMIC_STEREO, 128 oDMPerf.SetMasterAutoDownload True شي DirectMusicLoader8 کمک مي کند تا موزيک درون بافر load شود . شي DirectMusicSegment8 مموزيکي را که بايد پخش شود ذخيره مي کند . کد فوق کافي است يکبار زمانيکه برنامه آغاز مي شود ، اجرا گردد . اکنون ما يک واسط مقدار دهي شده از DirectMusic داريم اما قبل از اينکه موزيک را Load کرده و پخش کنيم چگونگي terminate کردن DirectMusic را در زير مي بينيد : If ObjPtr(oDMSeg)Then Set oDMSeg = Nothing If ObjPtr(oDMLoader)Then Set oDMLoader = Nothing If Not (oDMPerf Is Nothing) Then oDMPerf.CloseDown Set oDMPerf = Nothing End If If ObjPtr(oDX) Then Set oDX = Nothing پيغامها : در برخي از component هاي DirectX8 مثل Input , Sound , Music و Play برنامه شما بايستي يک سيستم messaging را برپا کند تا DirectX زمان وقوع برخي رخدادهاي خاص را بشما گزارش دهد . اين مطلب بخصوص زمانيکه يک موزيک را پخش مي کنيد مفيد است براي مثال مي تواند زمان خاتمه يافتن موزيک را به شما اطلاع دهد و آنگاه شما مي توانيد قطعه موزيک بعدي را پخش کنيد . پيغامها توسط يک سيستم callback انجام مي شوند . کد زير را در تابع InitDMusic تان پس از initial کردن DirectMusic8 قرار دهيد : oDMPerf.AddNotificationType DMUS_NOTIFY_ON_SEGMENT hEvent = oDX.CreateEvent(Me)x oDMPerf.SetNotificationHandle hEvent اولين سطر به DirectMusic مي گويد چه نوع پيغامهايي را مي خواهيد به برنامه تان بفرستد . چندين نوع پيغام وجود دارد : DMUS_NOTIFY_ON_SEGMENT = اطلاعات موزيک فعلي ( شروع پخش ، پايان پخش و غيره ) DMUS_NOTIFY_ON_CHORD = اطلاعات تغيير chord موزيک DMUS_NOTIFY_ON_COMMAND = زمانيکه يک event فرماني صدا زده شود . DMUS_NOTIFY_ON_MEASUREANDBEAT = اطلاعات beat/measure مربوط به موزيک فعلي DMUS_NOTIFY_ON_PERFORMANCE = که event مربوط به سطح performance می باشد . DMUS_NOTIFY_ON_RECOMPOSE = که recomposition event می باشد . آخرين بخش از پيغام دهي ، تابع اصلي آن مي باشد . همانطور که در بخش Initial کردن DirectMusic ديديد يک توصيف بصورت Implements DirectXEvent8 داشتيم . بخش اصلي تابع callback مربوط به DirectXEvent8 ، شامل يک select case است که بين پيغامهاي مختلف سوئيچ می کند : Private Sub DirectXEvent8_DXCallback(ByVal eventid As Long)x If eventid = hEvent Then Dim dmMSG As DMUS_NOTIFICATION_PMSG If Not oDMPerf.GetNotificationPMSG(dmMSG) Then Else Select Case dmMSG.lNotificationOption Case DMUS_NOTIFICATION_SEGABORT Case DMUS_NOTIFICATION_SEGALMOSTEND Case DMUS_NOTIFICATION_SEGEND Case DMUS_NOTIFICATION_SEGLOOP Case DMUS_NOTIFICATION_SEGSTART Case Else End Select End If End If End Sub پخش موزيک / متوقف کردن موزيک : براي پخش يک موزيک ابتدا بايستي آنرا load کنيد . اينکار توسط کد زير انجام مي شود : oDMLoader.SetSearchDirectory App.Path & "\"x Set oDMSeg = oDMLoader.LoadSegment(App.Path & FILENAME)oDMSeg.SetStandardMidiFile DirectMusic تنها چهار نوع فرمت صوتي را مي پذيرد : WAV ، MID ، RMI و SEG . براي پخش فايلهاي MP3 بايستي از DirectXShow استفاده کنيد که آنرا در درسهاي بعدي خواهيد ديد . اکنون که داده هاي فايل صوتي درون بافر load شد مي توانيد آنرا پخش کنيد : oDMSeg.SetRepeats 0 oDMPerf.PlaySegmentEx oDMSeg, DMUS_SEGF_DEFAULT, 0 تعداد پخش شدن فايل را با متد SetRepets تنظيم کنيد . اگر اين مقدار صفر باشد ، آهنگ تنها يکبار پخش مي شود و اگر 1- باشد بطور ممتد پخش خواهد شد . براي متوقف کردن موزيک از کد زير استفاده کنيد : oDMPerf.StopEx oDMSeg, 0, DMUS_SEGF_DEFAULT براي تنظيم ميزان صدا از متد SetMasterVolume استقاده کنيد : oDMPerf.SetMasterVolume yourvalue رنج صدا بين 20+ دسی بل تا 200- دسي بل است . براي تنظيم Tempo از متد SetMasterTempo استفاده کنيد : oDMPerf.SetMasterTempo yourvalue/ 100 بطور نرمال tempo برابر 1 مي باشد . عدد 2 سرعت را دو برابر مي کند و عدد 0 موزيک را قطع مي کند . موضوع : ايجاد صدای سه بعدی توسط DirectSound3D مقدمه تاکنون با چگونگي پخش افکتهاي صوتي و موسيقي پس زمينه توسط DirectXAudiuo آشنا شديد . اين مطالب براي کاربردهاي ساده مناسبند اما اينکه فقط ما صداي استريو داشته باشيم کافي نيست و در کاربردهاب حرفه اي بايستي از صداهاي کاملاً سه بعدي استفاده کنيم . با استفاده از افکتهاي صوتي سه بعدي مي توانيم صدا را در تمام جهتها براي کاربر شبيه سازي کنيم اما با همه مزاياي صداي سه بعدي ، دو اشکال براي آن وجود دارد : اول اينکه پخش صداي سه بعدي پيچيده تر از پخش صداي عادي است و تنها کارت هاي سخت افزاري جديد بطور کاملاً واقعي از آن پشتيباني مي کنند و دوم اينکه صداي سه بعدي با 4 بلندگو يا بيشتر حاصل مي شود – کيفيت حالت 2 بلندگو بد نيست اما در مقايسه با حالت 4 بلندگو ، بسيار کيفيت صداي سه بعدي پايين است . برپاسازي DirectSound3D برپاسازي صداي سه بعدي چندان پيچيده نيست اما هر بافر صوتي که براي يک صداي سه بعدي مي سازيد ، يک overhead را به سيستم تان اضافه مي کند . همچنين برخي درايورها هستند که تنها اجازه ايجاد تعداد محدودي بافر سه بعدي را در يک لحظه مي دهند و نيز اغلب درايورها تعداد بافرهاي سه بعدي که مي توان در يک لحظه پخش کرد را محدود مي کنند ( معمولاً 8 تا 16 بافر ) . اولين قدم در استفاده از صداي سه بعدي تعريف متغيرها و اشيا زير است : Dim DSBuffer As DirectSoundSecondaryBuffer8 Dim DSBuffer3D As DirectSound3DBuffer8 Dim DSBListener As DirectSound3DListener8 تنها دو شي آخر براي شما جديد هستند . شي DirectSound3dBuffer8 يک ارائه سه بعدي از بافرهاي عادي است . ما همچنان از DirectSoundSecondaryBuffer8 براي نگهداري داده صوتي استفاده مي کنيم و از DirectSound3Dbuffer8 براي نگهداري پارامترهاي سه بعدي و تنظيمات سه بعدي استفاده مي کنيم . شي DirectSound3Dlistener8 نيز يک listener است و براي تنظيم کردن سرعت و جهت صدا و برخي پارامترهاي ديگر استفاده مي شود . مرحله دوم ، ساخت بافر صوتي است . اين کار در دو بخش انجام مي شود . اول ما يک بافر صوتي نرمال مي سازيم و سپس يک واسط بافر صوتي سه بعدي را از آن بدست مي آوريم : If Not (DSBuffer Is Nothing) Then DSBuffer.Stop Set DSBuffer = Nothing DSBDesc.lFlags = DSBCAPS_CTRL3D Or DSBCAPS_CTRLVOLUME Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\blip.wav", DSBDesc)x If DSBDesc.fxFormat.nChannels > 1 Then MsgBox "You can only use mono (1 channel) sounds with DirectSound3D"x End If If optLow.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_NO_VIRTUALIZATION If optMedium.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_HRTF_LIGHT If optHigh.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_HRTF_FULL Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\blip.wav", DSBDesc)x Set DSBuffer3D = DSBuffer.GetDirectSound3DBuffer()x سه نکته است که بايد به آن دقت شود : 1 – اضافه کردن DSBCAPS_CTRL3D بسيار مهم است . شما اگر اين پارامتر را بکار نبريد ، قادر نخواهيد بود که واسط سه بعدي را بدست آوريد . 2 – ما بايستي تنها از افکتهاي صوتي Mono ( تک کاناله ) استفاده کنيم زيرا افکت صوتي استريو در صداي سه بعدي معنا ندارد زيرا صدا از يک نقطه در فضاي سه بعدي مي آيد . 3 – سطح الگوريتم سه بعدي – که در پارامتر DSBDesc.guid3Dalgorhthm آمده . حالت NO VIRTULIZATION تنها از CPU استفاده مي کند و روي تمام سيستم ها کار مي کند اما افکتها مينيمم هستند . حالت HRTF LIGHT هم از CPU و هم سخت افزار کارت صوتي استفاده مي کند و کيفيت بهتري را نسبت به خالت اول ارائه مي دهد . حالت HRTF FULL بهترين حالت است اما در صورتي درست کار مي کند که يک سخت افزار سه بعدي داشته باشيد . آخرين پارامتري که بايد تنظيم کنيم شي listener است : DSBDesc_2.lFlags = DSBCAPS_CTRL3D Or DSBCAPS_PRIMARYBUFFER Set DSBPrimary = DS.CreatePrimarySoundBuffer(DSBDesc_2) x Set DSBListener = DSBPrimary.GetDirectSound3Dlistener DSBListener.SetOrientation 0#, 0#, 1#, 0#, 1#, 0#, DS3D_IMMEDIATE تا اينجا صداي سه بعدي ما آماده است و مي توانيم برخي پخش بافر را مشابه درسهاي قبلي شروع کنيد . پارامترهاي اختياري : چند پارامتر وجود دارد که مي توان آنها را تغيير داد : 1 – Volume : عدد 0 بيشترين ميزان صدا و عدد 3000 - کمترين ميزان صدا را دارد : If DSBuffer Is Nothing Then Exit Sub DSBuffer.SetVolume scrlVolume.Value 2 – Position : تنظيم محل listener : DSBuffer3D.SetPosition Src_X, 0, Src_Y, DS3D_IMMEDIATE DSBListener.SetPosition Src_X, 0, Src_Y, DS3D_IMMEDIATE 3 – Velocity : تنظيم سرعت و جهت منبع صدا : DSBuffer3D.SetVelocity X, Y, Z, DS3D_IMMEDIATE DSBListener.SetVelocity X, Y, Z, DS3D_IMMEDIATE 4 – Dppler Effect : انحراف صدا از مسيري که مي پيمايد انحراف سرعت حرکت صدا : DSBListener.SetDopplerFactor CSng(scrlDoppler.Value), DS3D_IMMEDIATE 5 – Rolloff Effect : rolloff چگونگي تضعيف صدا با تغيير فاصله است DSBListener.SetRolloffFactor CSng(scrlRolloff.Value), DS3D_IMMEDIATE 6 – Distance : ماکزيمم فاصله اي که يک صدا مي تواند شنيده شود : DSBuffer3D.SetMaxDistance 250, DS3D_IMMEDIATE DSBuffer3D.SetMinDistance 0.01, DS3D_IMMEDIATE رجيستري چيست ؟ سيستم عامل ويندوز تنظيمات سخت افزاري و نرم افزاري خود را بطور مرکزي در يک بانک اطلاعاتي با ساختار سلسله مراتبي ذخيره مي کند که رجيستري نام دارد . رجيستري جايگزيني براي بسياري از فايلهاي پيکربندي INI ، SYS و COM است که در نسخه هاي اوليه ويندوز موجود بود . رجيستري ، سيستم عامل را با مهيا کردن اطلاعات موردنيز براي اجراي برنامه ها و load شدن component ها ، کنترل مي کند . رجيستري شامل انواع مختلفي از اطلاعات مي باشد مثل : - اطلاعات سخت افزارهاي نصب شده روي سيستم - اطلاعات درايورهاي نصب شده روي سيستم - اطلاعات برنامه هاي نصب شده روي سيستم - اطلاعات پروتکلهاي شبکه اي مورد استفاده در سيستم ساختار رجيستري شامل چندين مجموعه رکورد است که داده هاي اين رکوردها توسط بسياري از برنامه ها و اجزاي سيستم عامل خوانده و يا نوشته مي شود . اجزاي رجيستري اجزاي تشکيل دهنده رجيستري عبارتند از : 1 – subtree : Subtree ها همانند folder هاي موجود در ريشه يک درايو هارد هستند . رجستری ويندوز داراي پنج subtree مي باشد : - HKEY_LOCAL_MACHINE : شامل تمام داده هاي پيکربندي براي کامپيوتر مي باشد و شامل 5 key است :Hardware ، SAM ، Security ، Software و System - HKEY_USERS : شامل داده هاي مربوط به تنظيمات سيستم عامل براي هر user است مثل تنظيمات desktop و محيط ويندوز - HKEY_CURRENT_USER : شامل داده هاي کاربر فعلي سيستم - HKEY_CLASSES_ROOT : شامل اطلاعات پيکربندي نرم افزار است مثل داده هاي OLE و داده هاي کلاسهاي متناظر با فايل - HKEY_CURRENT_CONFIG : شامل اطلاعات مورد نياز براي تنظيمات داريورهاي سخت افزاري و غيره 2 – Key : key ها همانند folder ها و subfolder هاي روي هارد هستند . هر key متناظر با object هاي نرم افزاري يا سخت افزاري مي باشد . subkey ها key هايي هستند که درون يکسري key قراردارند . 3 – Entry : هر key داراي يک يا چند entry است . هر entry داراي سه بخش مي باشد : - نام Name - نوع داده اي Data Type : مقدار هر entry يکي از انواع داده هاي زير است : REG_DWORD ، REG_SZ ، REG_EXPAND_SZ ، REG_BINARY ، REG_MULTI_SZ ، REG_FULL_RESOURCE_DESCRIPTOT - مقدار Value نکته 1 : براي مشاهده رجيستري و اعمال تغييرات در آن ( لطفاً اگر هيچ تجربه اي در تنظيم کردن رجيستري نداريد اطلاعات آنرا تغيير ندهيد ) ، مي توانيد از برنامه regedit.exe و يا regedt32.exe موجود در ويندوز استفاده کنيد . براي اينکار کافيست نام برنامه را در کادر Run وارد کنيد . براي کار با رجيستري در ويژوال بيسيک کلاس Registery.bas را مطابق مطالب زير ايجاد کرده و در پروژه هاي خود از آن استفاده کنيد : 1 - تعريف ثابتهاي مورد نياز : براي نوشتن اين کلاس نياز به تعريف چهار دسته ثابت داريم : - ثابتهاي مربوط به تعريف data type هاي entry هاي رجيستري : Global Const REG_SZ As Long = 1 Global Const REG_DWORD As Long = 4 - ثابتهاي مربوط به تعريف key هاي رجيستري Global Const HKEY_CLASSES_ROOT = &H80000000 Global Const HKEY_CURRENT_USER = &H80000001 Global Const HKEY_LOCAL_MACHINE = &H80000002 Global Const HKEY_USERS = &H80000003 - ثابتهاي مربوط به خطاهاي کار با رجيستري Global Const ERROR_NONE = 0 Global Const ERROR_BADDB = 1 Global Const ERROR_BADKEY = 2 Global Const ERROR_CANTOPEN = 3 Global Const ERROR_CANTREAD = 4 Global Const ERROR_CANTWRITE = 5 Global Const ERROR_OUTOFMEMORY = 6 Global Const ERROR_INVALID_PARAMETER = 7 Global Const ERROR_ACCESS_DENIED = 8 Global Const ERROR_INVALID_PARAMETERS = 87 Global Const ERROR_NO_MORE_ITEMS = 259 - ثابتهاي متفرقه Global Const KEY_ALL_ACCESS = &H3F Global Const REG_OPTION_NON_VOLATILE = 0 2 - Declare کردن Api هاي مورد نياز : براي کار با رجيستري از توابع کتابخانه Advapi32.dll استفاده مي کنيم . اين توابع عبارتند از : - تابع RegCloseKey : آزاد کردن handle مربوط به يک key Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long - تابع RegCreateKeyEx : ساخت يک key در رجيستري ( اگر key قبلاً وجود داشته باشد ، اين تابع آنرا باز مي کند ) : Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal lpClass As String, ByVal dwOptions As Long, ByVal samDesired As Long, ByVal lpSecurityAttributes As Long, phkResult As Long, lpdwDisposition As Long) As Long - تابع RegOpenKeyEx : باز کردن يک key Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long - تابع RegQueryValueExLong : استخراج type و data ي يک نام متناظر با يک key باز شده Declare Function RegQueryValueExString Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As String, lpcbData As Long) As Long Declare Function RegQueryValueExLong Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As Long, lpcbData As Long) As Long Declare Function RegQueryValueExNULL Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As Long - تابع RegSetValueEx : ذخيره يک مقدار در فيلد value يک کليد باز Declare Function RegSetValueExString Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, ByVal lpValue As String, ByVal cbData As Long) As Long Declare Function RegSetValueExLong Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpValue As Long, ByVal cbData As Long) As Long - تابع RegDeleteKey : پاک کردن يک کليد و کليه اطلاعات مرتبط با آن Private Declare Function RegDeleteKey& Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpSubKey As String) - تابع RegDeleteValue : حذف مقدار يک key Private Declare Function RegDeleteValue& Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As Long, ByVal lpValueName As String) 3 - توابع کمکي : براي نوشتن توابع اصلي کار با رجيستري نياز به نوشتن توابع کمکي زير است : - تابع SetValueEx : با توجه به نوع داده يک کليد ، مقدار موجود در آنرا در يک متغير ذخيره مي کند : Public Function SetValueEx(ByVal hKey As Long, sValueName As String, lType As Long, vValue As Variant) As Long Dim lValue As Long Dim sValue As String Select Case lType Case REG_SZ ' type of value is string sValue = vValue SetValueEx = RegSetValueExString(hKey, sValueName, 0&, lType, sValue, Len(sValue))x Case REG_DWORD ' type of value is Double word lValue = vValue SetValueEx = RegSetValueExLong(hKey, sValueName, 0&, lType, lValue, 4)x End Select End Function - تابع QueryValueEx : سايز و نوع داده اي يک داده را که بايد خوانده شود مشخص مي کند . Function QueryValueEx(ByVal lhKey As Long, ByVal szValueName As String, vValue As Variant) As Long Dim cch As Long Dim lrc As Long Dim lType As Long Dim lValue As Long Dim sValue As String lrc = RegQueryValueExNULL(lhKey, szValueName, 0&, lType, 0&, cch)x Select Case lType ' For strings Case REG_SZ: sValue = String(cch, 0)x lrc = RegQueryValueExString(lhKey, szValueName, 0&, lType, sValue, cch)x If lrc = ERROR_NONE Then vValue = Left$(sValue, cch)x Else vValue = Empty End If ' For DWORDS Case REG_DWORD: lrc = RegQueryValueExLong(lhKey, szValueName, 0&, lType, lValue, cch)x If lrc = ERROR_NONE Then vValue = lValue Case Else 'all other data types not supported lrc = -1 End Select QueryValueExExit: QueryValueEx = lrc Exit Function QueryValueExError: Resume QueryValueExExit End Function 4 - توابع اصلي : توابع مربوط به پاک کردن يک کليد از رجيستري ، ساخت يک کليد جديد در رجيستري و مقداردهي به يک کليد : - تابع DeleteKey : اين تابع يک کليد از رجيستري را حذف مي کند . داراي دو پارامتر ورودي است : Location که يکي از مقادير HKEY_CLASSES_ROOT ، HKEY_CURRENT_USER ، HKEY_LOCAL_MACHINE و يا HKEY_USERS است . KeyName که نام کليدي است که بايد از رجيستري حذف شود . اين کليد ممکنست شامل subkey هايي نيز باشد مثلاً Key1\SubKey1 Public Function DeleteKey(lPredefinedKey As Long, sKeyName As String)x Dim lRetVal As Long lRetVal = RegDeleteKey(lPredefinedKey, sKeyName)x DeleteKey = lRetVal ' return function value End Function - تابع DeleteValue : اين تابع يک entry را از کليد حذف مي کند . داراي سه پارامتر ورودي است : Location ، KeyName و ValueName که نام آن value را مشخص مي کند . Public Function DeleteValue(lPredefinedKey As Long, sKeyName As String, sValueName As String)x Dim lRetVal As Long Dim hKey As Long lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x lRetVal = RegDeleteValue(hKey, sValueName)x RegCloseKey (hKey)x DeleteValue = lRetVal End Function - تابع CreateNewKey : اين تابع يک کليد جديد ايجاد مي کند . داراي دو پارامتر ورودي است : Location و KeyName Public Function CreateNewKey(lPredefinedKey As Long, sNewKeyName As String)x Dim hNewKey As Long Dim lRetVal As Long lRetVal = RegCreateKeyEx(lPredefinedKey, sNewKeyName, 0&, vbNullString, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0&, hNewKey, lRetVal)x RegCloseKey (hNewKey)x CreateNewKey = lRetVal End Function - تابع SetKeyValue : اين تابع پارامتر data يک entry را تنظيم مي کند . داراي 5 پارامتر ورودي است : Location ، KeyName ، ValueName ، ValueSetting و ValueType Public Function SetKeyValue(lPredefinedKey As Long, sKeyName As String, sValueName As String, vValueSetting As Variant, lValueType As Long)x Dim lRetVal As Long Dim hKey As Long lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x lRetVal = SetValueEx(hKey, sValueName, lValueType, vValueSetting)x RegCloseKey (hKey)x SetKeyValue = lRetVal End Function - تابع QueryValue : اين تابع فيلد داده يک entry را برمي گرداند . داراي سه پارامتر ورودي است : Location ، KeyName و ValueName Public Function QueryValue(lPredefinedKey As Long, sKeyName As String, sValueName As String)x Dim lRetVal As Long Dim hKey As Long Dim vValue As Variant lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x lRetVal = QueryValueEx(hKey, sValueName, vValue)x QueryValue = vValue RegCloseKey (hKey)x End Function ساخت يک انتصاب فايل يا File Association به يک برنامه در اين درس می خواهم با استفاده از کلاسی که در درس قبل معرفی شد تابعی بسازيم که توسط آن بتوانيم فايلهای با پسوندی مشخص را به يک برنامه اختصاص دهيم . بعبارت ديگر تابعی بنويسيم که اطلاعات لازم برای باز شدن فايلهايی با پسوند xxx را توسط برنامه MyApp.exe در رجيستری ثبت کند . Public Sub CreateAssociation(sExtension As String, sApplication As String, sAppPath As String)x Dim sPath, sAppExe As String CreateNewKey "." & sExtension, HKEY_CLASSES_ROOT SetKeyValue HKEY_CLASSES_ROOT, "." & sExtension, "", sApplication & ".Document", REG_SZ CreateNewKey sApplication & ".Document\shell\open\command", HKEY_CLASSES_ROOT SetKeyValue HKEY_CLASSES_ROOT, sApplication & ".Document", "", sApplication & " Document", REG_SZ sPath = sAppPath & " %1"x sAppExe = sApplication & ".exe"x SetKeyValue HKEY_CLASSES_ROOT, sApplication& ".Document\shell\open\command", "", sPath, REG_SZ CreateNewKey "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\." & sExtension, HKEY_CURRENT_USER SetKeyValue HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\." & sExtension, "Application", sAppExe, REG_SZ CreateNewKey "Applications\" & sAppExe & "\shell\open\command", HKEY_CLASSES_ROOT SetKeyValue HKEY_CLASSES_ROOT, "Applications\" & sAppExe & "\shell\open\command", "", sPath, REG_SZ End Sub کاربرد اين تابع بصورت زير است : CreateAssociation("xxx","MyApp","c:\MyApp.exe")x اجرا شدن يک برنامه در هنگام راه اندازی سيستم فرض کنيد می خواهيم برنامه ای بنويسيم که هر بار در هنگام راه اندازي سيستم بطور خودكار اجرا شود. البته نمي خواهم در startup ويندوز ديده شود . براي اين كار بايد برنامه موردنظر را در StartUp رجيستري قرار دهيم . به اين ترتيب كه در يكي از كليدهاي زير يك مقدار رشته اي جديد(String Value) ايجاد کنيم و آدرس برنامه را در آن وارد كنيم : HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run براي مثال اگه اسم برنامه مورد نظر MyApp و مسيرش C:\Windows\MyApp.exe است بايد بصورت زير عمل کرد : SetKeyValue HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "MyApp", "C:\MyApp.exe", REG_SZ نکته : البته دو تا راه ديگر برای اينکار وجود دارد که برخی تروجان ها هم از اين روشها استفاده می کنند تا روی سيستم باقی بمانند : يكي استفاده از win.ini و نوشتن نام فايل جلوي = run و ديگري استفاده از system.ini و نوشتن نام برنامه جلوي خط explorer.exe . آشنايي با Windows API قصد دارم در مورد API هاي ويندوز و چگونگي استفاده از آنها در ويژوال بيسيک بطور خلاصه توضيح دهم و همچنين دو مثال پراستفاده را نيز در اين زمينه بيان کنم که عبارتند از چگونگي پخش فايلهاي Wav و ساخت يک تايمر با دقت بالا : ۱ - آشنايي با Windows API : واژه API مخفف Application Programming Interface مي باشد . API هاي ويندوز مجموعه اي از توابع از پيش آماده موجود در سيستم عامل هستند که شما مي توانيد آنها را در برنامه هاي خود فراخواني کنيد . اين توابع در چندين کتابخانه DLL ويندوز ذخيره شده اند . براي دسترسي به اين توابع در ويژوال بيسيک ابتدا بايد آنها را برنامه خود declare کنيد . براي مثال : Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long همانطور که مي بينيد مثال فوق يک Declare از تابع sndPlaySound مي باشد که اين تابع در کتابخانه Winmm.dll موجود است . کلمه Alias نشان مي دهد که اين تابع نام ديگري در dll دارد . ساير بخشها مربوط به تعريف پارامترهاي تابع مي باشند که در مورد مثال فوق ، اين تابع دو پارامتر ورودي و يک خروجي از نوع Long دارد . پس از Delare کردن API در برنامه مي توانيد از آن استفاده نمائيد . ۲ - پخش فايلهاي Wav : تابعي که براي پخش فايلهاي Wav استفاده مي شود تابع sndPlaySound است که در بالا با آن آشنا شديد . پارامتر lpzSoundName نام و مسير فايل Wavو پارامتر uFlags چگونگي پخش فايل را مشخص مي کند . مقادير ممکن اين پارامتر عبارتند از : - SND_ASYNC : اجازه مي دهد طوري فايل Wav پخش شود که آنرا بتوان وقفه داد . بعبارت ديگر قادر خواهيد بود فايل Wav تان را هر زمان که بخواهيد پخش کنيد و مطمئن باشيد که حتماً شنيده مي شود . - SND_LOOP : فايل Wav را بطور ممتد پخش مي کند . - SND_NODEFAULT : اگر فايل Wav پيدا نشود صداي ديگري پخش نخواهد شد ( مثلاً برخي صداهاي default ويندوز ) - SND_SYNC : در طول پخش فايل Wav کنترل به برنامه داده نمي شود . اين پارامتر در زمانيکه مي خواهيد فايل Wav اي را در پس زمينه برنامه تان پخش کنيد مناسب نمي باشد . - SND_NOSTOP : اگر فايل Wav اي قبلاً در حال پخش باشد ، فايل Wav شما آنرا دچار وقفه نمي کند . از اين پارامتر زماني استفاده مي شود که بخواهيم فايل Wav مان هيچوقت در وسط کار قطع نشود . اگر بخواهيد از بيش از يکي از اين پارامترها استفاده کنيد توسط Or آنها را ترکيب نمائيد مثال : sndPlaySound App.path & "\ding.wav", SND_ASYNC or SND_LOOP نکته : براي استفاده از توابع صوتي پيچيده تر بايستي از DirectSound که يکي از اجزاي DirectX مي باشد استفاده کنيد . در مورد DirectSound بعداً صحبت خواهم کرد . ۳ - ساخت يک تايمر با دقت بالا : شايد تا بحال از کنترل تايمر موجود در نوار ابزار ويژوال بيسيک استفاده کرده باشيد . اين تايمر داراي دقت حدود ۵۵ ميلي ثانيه است . براي دستيابي به زمانهاي با دقت بالاتر اين کنترل مفيد نخواهد بود . تابع GetTickCount يک API موجود در کتابخانه Kernel32.dll است . اين تابع طول زماني را که سيستم شروع به کار کرده است را برحسب ميلي ثانيه برمي گرداند : Private Declare Function GetTickCount Lib "kernel32" () As Long براي بررسي طي شدن يک مدت زماني خاص شما ابتدا بايد مقدار اين تابع را در يک متغير کمکي مثل TempTime قرار دهيد سپس در يک حلقه Do-Loop بايد اختلاف زمان GetTickCount جديد و زمان TempTime را با مقدار زماني که مي خواهيد سپري شود مقايسه کنيد : TempTime = GetTickCount()x Do While DesiredTime < GetTickCount() - TempTime Do some things' Loop توسط کد بالا مي توان يک عمليات خاص را براي يک مدت زماني مشخص اجرا کرد . کد زير نشان مي دهد که چگونه مي توان دستورات خاصي را در فواصل زماني خاص اجرار کرد : ExitFunction = False TempTime = GetTickCount()x Do While not(ExitFunction)x If DesiredTime < GetTickCount() - TempTime then Reset the temporary variable' TempTime = GetTickCount()x Do some things' End If Loop همچنين از تابع GetTickCount مي توان براي benchmark برنامه ها استفاده کرد . بعبارت ديگر مي توان زمان اجراي يکسري دستورات خاص را بدست آورد مباحث پيشرفته Direct3D موضوع : ساخت يک موتور گرافيکي سه بعدي قبل از شروع مباحث جديد برنامه نويسي Direct3D ، با هم مروري بر مباحث قبلي خواهيم داشت .( مباحث قبلي در آرشيو موجود مي باشند در اين درس با استفاده از مطالب قبلي يک Engine سه بعدي ساخته و از امکانات آن در يک برنامه نمونه استفاده خواهيم کرد . اين engine داراي دو کلاس است : 1 – کلاس MainD3D 2 – کلاس D3Dobject در کلاس MainD3D متغيرها و توابع لازم براي ساخت يک device سه بعدي ، تنظيمات ماتريسي ، تابع رندر و غيره موجود مي باشد . متغيرهاي عمومي اين کلاس عبارتند از : Public g_DX As New DirectX8 Public g_D3D As Direct3D8 Public g_D3DX As New D3DX8 Public g_D3DDevice As Direct3DDevice8 Public NTextures As Long روتين ها و توابع اين کلاس عبارتند از : 1 - InitD3D : اين روتين ، اشيا D3D و D3Ddevice را مي سازد و پارامترهاي آنها را تنظيم مي کند . 2 – ApplyCameraChanges : روتين ايجاد ماتريس View 3 – SetupMatrices : روتين ايجاد ماتريس Projection 4 – StartRender : در اين روتين عمليات لازم براي شروع عمل رندر صورت مي گيرد . 5 – RenderObject : اين تابع ، يک شي سه بعدي از نوع کلاس D3Dobject را مي گيرد و بردارهاي مورد نياز و نيز بافت شي را تنظيم مي کند و در پايان شي را ترسيم مي کند . 6 – FinishRender : در اين روتين به عمليات رندر پايان داده مي شود . 7 – Cleanup: روتين از بين بردن اشيا Direct3D 8 – CreateVector : تابع ساخت يک بردار سه بعدي 9 – CreateTextures : روتين ساخت يک بافت جديد 10 – InitTexture: تابع مقداردهي به يک بافت در کلاس D3Dobject متغيرها و توابع لازم براي ايجاد يک شي سه بعدي و اختصاص بافت به آن موجود مي باشد . در اين کلاس دو type عمومي تعريف شده است : 1 - NormalVERTEX 2 - TeturedVERTEX همچنين روتين ها و توابع اين کلاس عبارتند از : 1 – InitObject : تابعي که تنظيمات اوليه vertex ها و بافت شي را انجام مي دهد . 2 – Vertex : روتين ايجاد vertex هاي مورد نياز 3 – GetRenderingMode: تابعي که مد رندر را مشخص مي کند . و نيز يکسري تابع ساخت vertex نرمال و ساخت vertex داراي بافت و غيره اين دو کلاس در يک پروژه ويژوال بيسيک قرارداده شده و پروژه با نام D3Dengine.dll کامپايل شده است . حال با استفاده از اين engine مي خواهيم يک منظره سه بعدي را ايجاد کنيم : اين منظره شامل سه object است : ديوار ، آسمان و زمين. ابتدا بايد يک شي از کلاس MainD3D تعريف کنيم : Dim D3D8Main As MainD3D8 در متد Form Load نيز سه شي Floor ، Sky و Wall را بصورت زير تعريف مي کنيم : Dim Floor As D3DObject Dim Sky As D3DObject Dim Walls As D3Dobject سپس اين سه شي را به اضافه شي D3D8Main ، ايجاد مي کنيم : Set D3D8Main = New D3DEngine.MainD3D8 Set Floor = New D3DEngine.D3DObject Set Sky = New D3DEngine.D3DObject Set Walls = New D3DEngine.D3Dobject در ابتدا شي MainD3D را Initial مي کنيم و سپس بافتهاي مورد نيز خود را مي سازيم : D3D8Main.InitD3D True, Me.hWnd D3D8Main.CreateTextures 3 D3D8Main.InitTexture 1, App.Path + "\floor.jpg" D3D8Main.InitTexture 2, App.Path + "\sky.bmp" D3D8Main.InitTexture 3, App.Path + "\wall.bmp" حال به سراغ ايجاد و مقداردهي vertex هاي floor مي رويم . floor شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد : Floor.InitObject 6, 2, TriangleList, True, 1 Floor.Vertex 0, -55, -2, -55, vbWhite, 0, 10 Floor.Vertex 1, 55, -2, -55, vbWhite, 10, 10 Floor.Vertex 2, 55, -2, 55, vbWhite, 10, 0 Floor.Vertex 3, -55, -2, -55, vbWhite, 0, 10 Floor.Vertex 4, 55, -2, 55, vbWhite, 10, 0 Floor.Vertex 5, -55, -2, 55, vbWhite, 0, 0 سپس به سراغ ايجاد و مقداردهي vertex هاي wall مي رويم . wall شامل بيست و چهار vertex مي باشد و بنابراين هشت face مثلثي دارد : Walls.InitObject 24, 8, TriangleList, True, 3 Walls.Vertex 0, -55, -2, -55, &HBCE8FC, 0, 1 Walls.Vertex 1, 55, -2, -55, &HBCE8FC, 5, 1 Walls.Vertex 2, 55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 3, -55, -2, -55, &HBCE8FC, 0, 1 Walls.Vertex 4, 55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 5, -55, 8, -55, &HBCE8FC, 0, 0 Walls.Vertex 6, -55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 7, 55, -2, 55, &HBCE8FC, 5, 1 Walls.Vertex 8, 55, 8, 55, &HBCE8FC, 5, 0 Walls.Vertex 9, -55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 10, 55, 8, 55, &HBCE8FC, 5, 0 Walls.Vertex 11, -55, 8, 55, &HBCE8FC, 0, 0 Walls.Vertex 12, -55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 13, -55, -2, -55, &HBCE8FC, 5, 1 Walls.Vertex 14, -55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 15, -55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 16, -55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 17, -55, 8, 55, &HBCE8FC, 0, 0 Walls.Vertex 18, 55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 19, 55, -2, -55, &HBCE8FC, 5, 1 Walls.Vertex 20, 55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 21, 55, -2, 55, &HBCE8FC, 0, 1 Walls.Vertex 22, 55, 8, -55, &HBCE8FC, 5, 0 Walls.Vertex 23, 55, 8, 55, &HBCE8FC, 0, 0 حال به سراغ ايجاد و مقداردهي vertex هاي sky مي رويم . sky شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد : Sky.InitObject 6, 2, TriangleList, True, 2 Sky.Vertex 0, -55, 8, -55, &HBCE8FC, 0, 1 Sky.Vertex 1, 55, 8, -55, &HBCE8FC, 0, 1 Sky.Vertex 2, 55, 8, 55, &HBCE8FC, 0, 1 Sky.Vertex 3, -55, 8, -55, &HBCE8FC, 0, 1 Sky.Vertex 4, 55, 8, 55, &HBCE8FC, 0, 1 Sky.Vertex 5, -55, 8, 55, &HBCE8FC, 0, 1 در پايان تابع رندر را صدا مي کنيم . البته در هر بار عمل رندر کردن ، دوربين يک درجه در صفحه X-Z دوران مي کند تا کل ديوار قابل مشاهده باشد : Dim Angle As Double PI = 3.1415 Angle = 0 Do DoEvents D3D8Main.StartRender vbBlack D3D8Main.RenderObject Sky D3D8Main.RenderObject Floor D3D8Main.RenderObject Walls D3D8Main.FinishRender If Sqr(Angle ^ 2) = 360 Then Angle = 0 Angle = Angle + 1 D3D8Main.CamLookAtX = Sin((Angle * 2 * PI) / 360) D3D8Main.CamLookAtZ = Cos((Angle * 2 * PI) / 360) D3D8Main.ApplyCameraChanges Loop موضوع : استفاده از object هاي 3D Studio Max در Direct3D تا بحال ما هر شيي را که مي خواستيم در Direct3D بسازيم خودمان بوسيله کد نويسي آنرا توصيف کرده ايم . ممکنست اين سوال برايتان پيش آمده باشد که بازيهاي تجاري براي توليد کاراکترهاي و اشيا پيچيده سه بعدي چگونه عمل مي کنند ؟ منطقي بنظر نمي رسد که اينگونه مدلهاي پيچيده بصورت کد وارد برنامه شده اند زيرا نياز به هزاران خط برنامه براي هر فريم خواهد بود . بجاي اينکار ما object هاي خود را توسط برنامه هاي ديگري مي سازيم و آنها را در برنامه خودمان load مي کنيم سپس بافتها و material هاي مورد نظر را به آنها اختصاص داده و در پايان آنها را رندر مي کنيم . مزيت ديگر اينکار اينست که شما مي توانيد براحتي فايل object خود را تغيير دهيد و مدلهايي با جزئيات متفاوت براي برنامه خود قرار دهيد . مراحل ساخت چنين برنامه هايي بصورت زير است : ۱ - ساخت object سه بعدي : اولين چيزي که بايستي بدانيد داشتن دانش پايه اي از چگونگي مدلسازي سه بعدي است . همچنين نياز به يک نرم افزار مدلسازي مثل 3D Studio Max داريد . بعد از ساخت مدل خود در Max نياز به يک Convertor داريد تا فايلهاي Max را به فايلهاي Direct3D که با فرمت "X." هستند تبديل کنيد . Convertor هاي زيادي براي تبديل فايلهاي نرم افزارهاي مدلسازي به فايلهاي "X." وجود دارند که برخي از آنها عبارتند از : - برنامه PolyTrans3D System Translation - برنامه Deep Exploration 2.0 - برنامه Quick3D - برنامه 3DWin - DirectX Explorer Plugin - ابزارهاي موجود در DirectX 8.0 SDK که عبارتند از : برنامه Conv3DS براي تبديل فايلهاي 3DS به فايلهاي X DX SDK Exporter Plugin براي تبديل فايلهاي 3DS و Max به فايلهاي X از بين اين برنامه ها و plugin ها من برنامه Deep Exploration را به شما پيشنهاد مي کنم . 2 - Load کردن يک Object ساخته شده : زمانيکه فايل X شي مورد نظر را ساختيد ، load کردن آن در direct3D ساده است . براي اينکار نياز به يک مش داريم که اطلاعات شي ما را نگهداري کند : Dim Mesh As D3DXMesh همچنين براي اختصاص material و texture به شي ، نياز به تعريف متغيرهاي زير داريم : Dim MeshMaterial As D3DMATERIAL8 Dim MeshTexture As Direct3DTexture8 حال به سراغ بازنويسي روتين InitGeometry مي رويم : - تعريف متغيرهاي مورد نياز : Dim mtrlBuffer as D3DXBuffer Dim TextureFile as String Dim n as Long - گرفتن داده هاي شي از فايل X : Set Mesh=D3DX.LoadMeshFromX app.path&"\"&"yourfilename",D3DMESH_MANAGED,D3DDevice,Nothing,mtrlBuffer,n - استخراج اطلاعات materiasl شي و تنظيم پارامتر Ambient : D3DX.BufferGetMaterial mtrlBuffer,0,MeshMaterial MeshMaterial.Ambient=MeshMaterial.Diffuse - استخراج نام بافت بکار رفته براي شي : TextureFile=D3DX.BufferGetTextureName(mtrlBuffer,0)x - ساخت بافت : If TextureFile<>"" Then Set MeshTexture=D3DX.CreateTextureFromFile D3DDevice,app.path&"\"&TextureFile,128,128,D3DX_DEFAULT,0, D3DFMT_UNKNOWN,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,D3DX_FILTER_LINEAR,0,Byval 0,Byval 0 End If ۳ - رندر نمودن شي : رندر نمودن شي چندان مشکل نيست اما همچنان بايد ماتريسها و تبديلاتي را که مي خواهيد ، خودتان مديريت کنيد . D3DDevice.SetMaterial MeshMaterial D3DDevice.SetTexture 0,MeshTexture Mesh.DrawSubset 0 موضوع : مباحث تکميلي نورپردازي در Direct3D در بخش اول آموزش Direct3D با مباني نورپردازي آشنا شديد . در اين درس قصد دارم آن مباحث را کاملتر برايتان مطرح کنم . نورپردازي يکي از بخشهاي مهم طراحي يک بازي و يا يک انيميشن سه بعدي است . بمنظور پياده سازي نورپردازي يک صحنه ابتدا بايد با تئوري آن آشنا شويد . تئوري نورپردازي : نورپردازي در Direct3D تخميني از چگونگي عملکرد نور در دنياي واقعي مي باشد . چهار نوع اصلي نور در Direct3D قابل استفاده است ( همچنين شما مي توانيد خودتان انواع جديدي از نور ايجاد کنيد که موضوع ما نيست ) : ۱ - Point Light : توسط يک نقطه در فضاي سه بعدي ايجاد مي شود و داراي سه پارامتر رنگ ، دامنه و تضعيف مي باشد . دامنه يک نور مسافتي است که نور مي تواند طي کند . تضعيف ، مقدار کاهش نور در اثر افزايش مسافت مي باشد . نور نقطه اي در تمام جهات تششع مي کند - شبيه يک لامپ حبابي و يا يک شمع ۲ - Spot Light : داراي يک موقعيت و يک جهت است و تنها نور را در يک جهت خاص مي تاباند - شبيه يک چراغ قوه . اين نور داراي يک زاويه مخروطي و يک دامنه است . ۳ - Directional Light : داراي موقعيت نيست و براي پياده سازي نورهايي که از فاصله بسيار دور مي آيند - مثل خورشيد - مناسب است . ۴ - Ambient Light : اين نور تضمين مي کند که تمام vertex هاي يک صحنه تاريکتر از يک رنگ خاص نباشند . عملي کردن نورپردازي : ضمن اينکه اغلب کارت هاي گرافيک سه بعدي از نورپردازي پشتيباني مي کنند اما اين نکته بايد مورد توجه قرار گيرد که با افزايش تعداد نور در يک صحنه محاسبات Direct3D بيشتر مي شود و اين باعث کند شدن رندر صحنه خواهد شد و بنابراين کارت هاي گرافيکي سه بعدي نيز داراي يک ماکزيمم تعداد نور هستند - مثلاً ۱۶ نور در GeForce 2 - همچنين توجه داشته باشيد که نورهاي مختلف داراي زمان پردازشي متفاوتي هستند . نور ambient سريعترين زمان پردازشي را دارد ، سپس نورdirectional ، سپس نور point و کندترين آنها Spot Light است . همچنين نکته ديگري که بايد توجه کنيد دامنه نور است . اگر نور ، يک منطقه بزرگي را پوشش دهد بر تعداد زيادي از vertex ها تاثير مي گذارد و اين باعث افزايش محاسبات مي شود . نورپردازي Specular - که در درسهاي بعدي در مورد آن صحبت مي کنم و براي ايجاد اشيا درخشان استفاده مي شود - نيز زمان پردازشي زيادي دارد و بهتر است کمتر از آن استفاده شود . پارامتر ديگري که بايد در نظر بگيريد جزئيات هندسه شما مي باشد . هر چه پيچيدگي صحنه بيشتر باشد ، نورپردازي نيز زمان بيشتري را مصرف مي کند . سايه زني نيز يک بخش بسيار پيچيده در مدل سازي نور است و محاسبات آن بسيار زمان گير خواهد بود بنابراين Direct3D مستقيماً محاسبات سايه زني را انجام نمي دهد بلکه رنگ نور را بر مبناي جهت هر مثلث scale مي کند بنابراين قسمت پشتي يک شي که رو به نور نيست ، هيچ نوري را دريافت نمي کند . بردار نرمال : Direct3D هر vertex را بر مبناي بک بردار نرمال نورپردازي مي کند و نوري که يک vertex دريافت مي کند به زاويه بين نور و بردار نرمال آن vertex بستگي دارد . بردار نرمال توسط سه vertex يک face مثلثي ايجاد مي شود و اين بردار نرمال ساخته شده به vertex ها اختصاص مي يابد . بردار نرمال در واقع سمت يک مثلث را مشخص مي کند بنابراين اگر نور پشت مثلث باشد ، مثلث هيچ نوري را دريافت نميکند . بردار نرمال بايستي داراي طول ۱ باشد . مراحل توليد بردار نرمال يک face مثلثي : ۱ - مطمئن شويد که face در جهت عقربه هاي ساعت ساخته شده است . ۲ - يک بردار از vertex شماره صفر به vertex شماره يک بسازيد . ۳ - يک بردار از vertex شماره صفر به vertex شماره دو بسازيد . ۴ - حاصلضرب برداري ( cross droduct ) اين دو بردار را بدست آوريد . ۵ - نتيجه حاصلضرب را نرمال کنيد . Private Function GenerateTriangleNormals(p0 As UnlitVertex, p1 As UnlitVertex, p2 As UnlitVertex) As D3DVECTOR Dim v01 As D3DVECTOR Dim v02 As D3DVECTOR Dim vNorm As D3DVECTOR D3DXVec3Subtract v01, MakeVector(p1.X, p1.Y, p1.Z), MakeVector(p0.X, p0.Y, p0.Z)x D3DXVec3Subtract v02, MakeVector(p2.X, p2.Y, p2.Z), MakeVector(p0.X, p0.Y, p0.Z)x D3DXVec3Cross vNorm, v01, v02 D3DXVec3Normalize vNorm, vNorm GenerateTriangleNormals.X = vNorm.X GenerateTriangleNormals.Y = vNorm.Y GenerateTriangleNormals.Z = vNorm.Z End Function اگر دو face در يک vertex مشترک باشند ( مثل گوشه دو ديوار ) براي توليد نرمال اين vertex ابتدا نرمال دو face را با روش فوق بدست آوريد سپس دو بردار نرمال را با هم جمع کنيد و در پايان بردار حاصلجمع را نرمال کنيد . برپاسازي نورپردازي : اولين چيزي که قبل از برپاسازي نورپردازي بايستي اعمال کنيم تغيير ساختار vertex است . براي اينکار بايد پارامتر color را از ساختار vertex حذف و سه پارامتر را براي نگهداري نرمال اضافه کنيم : Private Type UnlitVertex X As Single Y As Single Z As Single nx As Single ny As Single nz As Single tu As Single tv As Single End Type Const Unlit_FVF = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)x همچنين بايد براي تمام vertex هاي شي خود بردار نرمال را محاسبه کنيد براي مثال اگر شي شما يک مکعب است براي هر ۱۲ face آن بردار نرمال را بدست آوريد . در زير من کد لازم براي ساخت نرمال يکي از اين face ها را نوشته ام : Cube2(0) = CreateVertex(-1, -1, 1, 0, 0, 0, 0, 0)x Cube2(1) = CreateVertex(1, 1, 1, 0, 0, 0, 1, 1)x Cube2(2) = CreateVertex(-1, 1, 1, 0, 0, 0, 0, 1)x vN = GenerateTriangleNormals(Cube2(0), Cube2(1), Cube2(2))x Cube2(0).nx = vN.X: Cube2(0).ny = vN.Y: Cube2(0).nz = vN.Z Cube2(1).nx = vN.X: Cube2(1).ny = vN.Y: Cube2(1).nz = vN.Z Cube2(2).nx = vN.X: Cube2(2).ny = vN.Y: Cube2(2).nz = vN.Z براي برپا سازي نور ابتدا بايستي يک material به device خود اضافه کنيد : Dim Mtrl As D3DMATERIAL8, Col As D3DCOLORVALUE Col.a = 1: Col.r = 1: Col.g = 1: Col.b = 1 Mtrl.Ambient = Col Mtrl.diffuse = Col D3DDevice.SetMaterial Mtrl سپس بايستي طوري device خود را تنظيم کنيد که نور شما را بشناسد - lights يک شي از نوع D3DLight8 است - يکبار که اين خط را بنويسيد مي توانيد از نور استفاده کنيد اما اگر خصوصيات نور را تغيير دهيد بايستي دوباره اين دستور را فراخواني کنيد : D3DDevice.SetLight 0, Lights حال بايد نور را روشن کنيد : D3DDevice.LightEnable 0, 1 و در پايان بايد به Direct3D بگوئيد که نورپردازي را براي شما انجام دهد : D3DDevice.SetRenderState D3DRS_LIGHTING, 1 چگونگي ايجاد يک نور : براي ايجاد هر يک از ۴ نوع اصلي نور بايد به روشي خاص عمل کنيد : ۱ - نورپردازي Ambient : اين نوع نورپردازي بسيار ساده است و تنها با فراخواني تابع SetRenderState ايجاد مي شود . رنگ ambient يک عدد هگزادسيمال بصورت RRGGBB است : D3DDevice.SetRenderState D3DRS_AMBIENT, &H202020 ۲ - نورپردازي Directional : داراي دو پارامتر رنگ و جهت مي باشد : Lights.Type = D3DLIGHT_DIRECTIONAL Lights.diffuse.r = 1 Lights.diffuse.g = 1 Lights.diffuse.b = 1 Lights.Direction = MakeVector(0, -1, 0)x 3 - نورپردازي Point : داراي سه پارامتر موقعيت ، رنگ و تضعيف مي باشد : Lights.Type = D3DLIGHT_POINT Lights.position = MakeVector(5, 0, 2)x Lights.diffuse.b = 1 Lights.Range = 100 Lights.Attenuation1 = 0.05 ۴ - نورپردازي Spot : اين نور داراي دو مخروط است که نقاط خارج مخروط اول روشنتر از نقاط داخل آن هستند . دو زاويه براي مخروط وجود دارد - زاويه داخلي theta و زاويه خارجي phi - که برحسب راديان هستند : Lights.Type = D3DLIGHT_SPOT Lights.position = MakeVector(-4, 0, 0)x Lights.Range = 100 Lights.Direction = MakeVector(1, 0, 0)x Lights.Theta = 30 * (Pi / 180)x Lights.Phi = 50 * (Pi / 180)x Lights.diffuse.g = 1 Lights.Attenuation1 = 0.05 موضوع : استفاده از Index Buffer براي ذخيره سازي اشکال سه بعدي مقدمه : مکعبي که در درسهاي قبلي ساختيم را درنظر بگيريد . با دانشي که اکنون داريد ، دو راه براي ساخت يک مکعب داريم : ۱ - استفاده از 36 عدد vertex براي تعريف face هاي مکعب ۲ - ساخت مکعب با استفاده از يک مدلساز و ذخيره آن با فرمت X روش اول غيرکارامد است زيرا شما بايستي از تعداد زيادي vertex براي يک شکل بسيار ساده استفاده کنيد . روش دوم مناسب است اما زمانيکه بخواهيم رنگها و بافتها را تغيير دهيم دچار مشکل خواهيم شد . روش جديدي که امروز در مورد آن صحبت مي کنم استفاده ار Index Buffer است . Index Buffer شامل يکسري عدد integer است که اين اعداد مرجعي براي vertex هاي ذخيره شده در يک Vertex Buffer هستند . براي مثال فرض کنيد يک Vertex Buffer شامل 8 عدد vertex داريم که يک مکعب را براي ما توصيف مي کند . ما مي توانيم يک Index Buffer با ۳۶ عضو بسازيم بطوريکه ترتيب اتصال vertex ها را براي ما مشخص کنند . مثلاً Index هاي ۰ و ۱و ۳ براي مشخص کردن face شماره ۱ مکعب بکار مي روند . بنابراين بجاي استفاده از ۳۶ عدد vertex مي توانيم مکعب را با ۸ عدد vertex و يک Index Buffer بسازيم . گرچه استفاده از Index Buffer بسيار کارامد است اما چندين محدوديت در استفاده از آن وجود دارد . مهمترين آنها اينست که تمام انديسهايي که يک vertex مشابه را share مي کنند بايستي خصوصيات مشابهي داشته باشند - موقعيت ، رنگ ، بافت و نرمال يکسان - براي مثال نمي توانيد مکعبي بسازيد که هر face آن يک رنگ داشته باشد . ساخت Index Buffer : ابتدا به متغيرهاي زير نياز داريم : Dim VBuffer as Direct3DVertexBuffer8 Dim IBuffer as Direct3DIndexBuffer8 Dim Vlist(0 to 7) as LITVERTEX Dim Ilist(0 to 35) as Integer تابع InitGeometry بصورت زير بازنويسي مي شود: ۱- توليد هشت vertex براي مکعب : Vlist(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x Vlist(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x Vlist(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x Vlist(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x Vlist(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x Vlist(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x Vlist(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x Vlist(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x ۲ - ايجاد Vertex Buffer توسط تابع CreateVertexBuffer : Set VBuffer = D3DDevice.CreateVertexBuffer(Len(Vlist(0)) * 8, 0, Lit_FVF, D3DPOOL_DEFAULT)x D3DVertexBuffer8SetData VBuffer, 0, Len(Vlist(0)) * 8, 0, Vlist(0)x ۳ - توليد index ها : front ' Ilist(0) = 0: Ilist(1) = 1: Ilist(2) = 2 Ilist(3) = 1: Ilist(4) = 3: Ilist(5) = 2 Right ' Ilist(6) = 2: Ilist(7) = 3: Ilist(8) = 6 Ilist(9) = 3: Ilist(10) = 7: Ilist(11) = 6 Back ' Ilist(12) = 6: Ilist(13) = 7: Ilist(14) = 4 Ilist(15) = 7: Ilist(16) = 5: Ilist(17) = 4 Left ' Ilist(18) = 4: Ilist(19) = 5: Ilist(20) = 0 Ilist(21) = 5: Ilist(22) = 1: Ilist(23) = 0 Top ' Ilist(24) = 1: Ilist(25) = 5: Ilist(26) = 3 Ilist(27) = 5: Ilist(28) = 7: Ilist(29) = 3 Bottom ' Ilist(30) = 2: Ilist(31) = 6: Ilist(32) = 0 Ilist(33) = 6: Ilist(34) = 4: Ilist(35) = 0 ۴ - ايجاد Index Buffer توسط تابع CreateIndexBuffer : Set IBuffer = D3DDevice.CreateIndexBuffer(Len(Ilist(0)) * 36, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT)x D3DIndexBuffer8SetData IBuffer, 0, Len(Ilist(0)) * 36, 0, Ilist(0)x تابع Render : براي رندر کردن اين مکعب دو روش وجود دارد : ۱ - استفاده از تابع DrawIndexedPrimitive : در اين روش از VBuffer و IBUffer و آرايه vertex ها استفاده مي شود : Public Sub Render()x D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0 D3DDevice.BeginScene D3DDevice.SetStreamSource 0, VBuffer, Len(Vlist(0))x D3DDevice.SetIndices IBuffer, 0 D3DDevice.DrawIndexedPrimitive D3DPT_TRIANGLELIST, 0, 36, 0, 12 D3DDevice.EndScene D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0 End Sub ۲ - استفاده از تابع DrawIndexedPrimitiveUP : در اين روش از آرايه هاي vertex و index استفاده مي شود : Public Sub Render()x D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0 D3DDevice.BeginScene D3DDevice.DrawIndexedPrimitiveUP D3DPT_TRIANGLELIST, 0, 8, 12, Ilist(0), D3DFMT_INDEX16, Vlist(0), Len(Vlist(0))x D3DDevice.EndScene D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0 End Sub موضوع : Vertex/Mesh Animation در اين درس در مورد روشهاي ساخت انيميشن در Direct3D صحبت خواهيم کرد . انيميشن در فضاي سه بعدي در حالتهاي مختلفي مي تواند ايجاد شود که بسته به engine گرافيکي شما و ابزارهايي که ايجاد کرده ايد ، دارد . سه روش اصلي ساخت انيميشن وجود دارد که عبارتند از : - Tween سازي دستي / درون يابي خطي ( manual tweening/linear interpolation ) - درون بابي برداري ( vector interpolation ) - درون يابي بر اساس فريم کليدي ( keyframe interpolation ) 1 – روش اول يکي از ساده ترين راههاي ساخت انيميشن است . اين روش در زمانيکه با مدلهاي پيچيده سر و کار داريد مناسب نيست – و يا مدلهايي با تعداد زيادي vertex – اين روش نوعي tween کردن است که از مزيت index buffer ها استفاده مي کند . درون يابي ، چگونگي تغييرات شيي در طول يک زمان مشخص مي باشد . در درسهاي قبلي شما درون يابي رنگ را روي يک شي ديديد که در آن يک رنگ بطور ملايم به رنگ ديگري تبديل مي شد ( fadeشدن ( . درون يابي خطي نيز مشابه آن است . براي درون يابي خطي از موقعيت A به موقعيت B از فرمول زير استفاده مي شود : (B*V)+A*(1-V) که A و B مختصاتهاي مبدا و مقصد هستند و V ضريب درون يابي است که عددي بين صفر و يک مي باشد . اين فرمول مختصات نقطه tween را در هر لحظه مشخص مي کند . همانطور که مي بينيد بکار بردن اين فرمول براي يک شي با تعداد زيادي vertex بسيار وقت گير بوده و fram rate را پايين مي آورد . تابع زير دو vertex و يک مقدار ضريب درون يابي را مي گيرد تا نقطه tween را محاسبه کند : Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX TweenVertices.X = (Dest.X * TweenAmount) + Source.X * (1# - TweenAmount)x TweenVertices.Y = (Dest.Y * TweenAmount) + Source.Y * (1# - TweenAmount)x TweenVertices.Z = (Dest.Z * TweenAmount) + Source.Z * (1# - TweenAmount)x TweenVertices.color = Source.color End Function اگر شما از vertex هاي UNLIT استفاده کنيد – vertex هايي با بردار نرمال – در اينصورت بايد کد فوق را تغيير دهيد و بايد tween را از نرمال مبدا به نرمال مقصد نيز انجام دهيد . همانطور که مي بينيد رنگ tween vertex نيز تنظيم شده است . در يک تابع tweening مناسبتر مي توانيد رنگها ، مختصات بافت و مقادير specular را نيز tween کنيد . محدوديتي که اين روش دارد اينست که خطي است و براي مدل کردن حرکتهاي غير خطي درست کار نمي کند . حال مي خواهيم از تابع tween استفاده کنيم تا يک مکعب را در يک انيميشن به يک هرم تبديل کنيم . ابتدا سه شي را بصورت زير تعريف مي کنيم : در ابتداي انيميشن ، شي current cube همان source cube است’ CubeVertices(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x CubeVertices(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x CubeVertices(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x CubeVertices(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x CubeVertices(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x CubeVertices(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x CubeVertices(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x CubeVertices(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x مکعب اوليه’ CubeVerticesSource(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x CubeVerticesSource(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x CubeVerticesSource(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x CubeVerticesSource(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x CubeVerticesSource(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x CubeVerticesSource(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x CubeVerticesSource(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x CubeVerticesSource(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x هرم مقصد’ CubeVerticesDest(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x CubeVerticesDest(1) = CreateLitVertex(-0.1, 1, -0.1, &HFF00&, 0, 0, 0)x CubeVerticesDest(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x CubeVerticesDest(3) = CreateLitVertex(0.1, 1, -0.1, &HFF00FF, 0, 0, 0)x CubeVerticesDest(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x CubeVerticesDest(5) = CreateLitVertex(-0.1, 1, 0.1, &HFFFF, 0, 0, 0)x CubeVerticesDest(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x CubeVerticesDest(7) = CreateLitVertex(0.1, 1, 0.1, &HFFFFFF, 0, 0, 0)x حال بايد در يک حلقه با استفاده از تابع twen پيکسلهاي CubeVertices را update کنيم : Private Sub UpdateAnimation()x Dim I As Integer به روز کردن پارامترهاي زمان و جهت' If AnimTweenDir = True Then AnimTweenFactor = AnimTweenFactor + (((GetTickCount() - LastTimeTweened) / 1000)*1#) LastTimeTweened = GetTickCount If AnimTweenFactor >= 1# Then AnimTweenFactor = 1# AnimTweenDir = False End If Else AnimTweenFactor = AnimTweenFactor - (((GetTickCount() - LastTimeTweened) / 1000)*1#) LastTimeTweened = GetTickCount If AnimTweenFactor <= 0# Then AnimTweenFactor = 0# AnimTweenDir = True End If End If به روز کردن اطلاعات vertex ها ' For I = 0 To 7 CubeVertices(I) = TweenVertices(CubeVerticesSource(I), CubeVerticesDest(I), AnimTweenFactor)x Next I به روز کردن بافر vertex’ If D3DVertexBuffer8SetData(VBuffer, 0, Len(CubeVertices(0)) * 8, 0, CubeVertices(0)) = D3DERR_INVALIDCALL Then GoTo Error: Exit Sub Error: Debug.Print “Error occured whilst updating the animation…”x End Sub زمان پايه انيميشن توسط عبارت زير تنظيم مي شود : (((GetTickCount() - LastTimeTweened) / 1000) * 1#) همانطور که مي دانيد دو نوع انيميشن وجود دارد : انيميشن بر مبناي frame و انيميشن بر مبناي زمان . در انيميشن بر مبناي frame شماره فريم با يک مقدار ثابت در زمان افزايش مي يابد اما اگر اينکار باعث مي شود کيفيت انيميشن در کامپيوترهاي با سرعت متفاوت تغيير کند . بنابراين انيميشن را بر مبناي زمان توليد کرده ايم . انيميشن هاي بر مبناي زمان بجاي " 1 فريم در هر سيکل " ، " 30 فريم در هر ثانيه " هستند 2 – روش دوم از توابع کتابخانه D3DX براي انجام عمل tweening استفاده مي کند و بنابراين بهبودي در سرعت انيميشن نسبت به روش بالا حاصل مي شود . با استفاده از کتابخانه D3DX مي توانيم عمل درون يابي خطي را براي تمام اجزا اصلي يک vertex انجام دهيم . ليست زير توابعي را براي اينکار نشان مي دهد : - تابع D3DXVec3Lerp : انجام درون يابي براي موقعيت و نرمال : D3DXVec3Lerp( VOut as D3DVECTOR, V1 as D3DVECTOR, V2 as D3DVECTOR, S as Single)x - VOut = The result of the interpolation - V1 = The source coordinates - V2 = The destination coordinates - S = The interpolation amount - between, but not limited to, 0.0 - 1.0 scale; where 0 is the source and 1 is the destination - تابع D3DXColorLerp : انجام درون يابي براي رنگهاي vertex : D3DXColorLerp( COut as D3DCOLORVALUE, C1 as D3DCOLORVALUE, C2 as D3DCOLORVALUE, S as Single)x - COut = The resulting colour - C1 = The source colour - C2 = The destination colour - S = The interpolant, on a 0.0 to 1.0 scale - تابع D3DXVec2Lerp : انجام درون يابي براي مختصاتهاي دوبعدي : - VOut = The result of this interpolation - V1 = The source coordinates - V2 = The destination coordinates - S = The interpolant on a 0.0 to 1.0 scale - تابع D3DXVec3Hermite : توليد يک مسير منحني که از دو نقطه کنترل عبور مي کند : D3DXVec3Hermite( VOut as D3DVECTOR, V1 as D3DVECTOR, T1 as D3DVECTOR, V2 as D3DVECTOR, T2 as D3DVECTOR, S as Single)x - VOut = The Result - V1 = The Source Coordinate - T1 = The Tangent at the Source coordinate, this is the direction and speed the line will leave the source point. - V2 = The Destination Coordinate - T2 = The Tangent at the Destination coordinate, this is the direction and speed the line will enter the destination point. - S = The Interpolant Value براي اينکه بتوانيم از کتابخانه D3DX استفاده کنيم بايد توصيف vertex هايمان را تغيير دهيم و بايستي يکسري مقادير ARGB اضافي را به ساختار vertex اضافه کنيم : Private Type LITVERTEX X As Single Y As Single Z As Single color As Long specular As Long tu As Single tv As Single ColorEx As D3DCOLORVALUE End Type حال تابع tween را بصورت زير مي نويسيم : Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX Dim vResult As D3DVECTOR Dim vResult2 As D3DVECTOR2 Tween کردن موقعيت vertex ها ‘ D3DXVec3Lerp vResult, MakeVector(Source.X, Source.Y, Source.Z), MakeVector(Dest.X, Dest.Y, Dest.Z), TweenAmount TweenVertices.X = vResult.X TweenVertices.Y = vResult.Y TweenVertices.Z = vResult.Z Tween کردن اطلاعات texture ’ D3DXVec2Lerp vResult2, MakeVector2D(Source.tu, Source.tv), MakeVector2D(Dest.tu, Dest.tv), TweenAmount TweenVertices.tu = vResult2.X TweenVertices.tv = vResult2.Y Tween کردن اطلاعات رنگ ‘ D3DXColorLerp TweenVertices.ColorEx, Source.ColorEx, Dest.ColorEx, TweenAmount With TweenVertices.ColorEx TweenVertices.color = RGB(.B * 255, .G * 255, .R * 255)x End With End Function نکته اي که بايد به آن توجه کنيد اينست که در تابع فوق براي اشاره به vertex ، يک بردار ساخته شده است ( توسط تابع MakeVector ) . 3 – روش سوم پر استفاده ترين روش انيميشن سازي است . اگر شما انيميشن هاي پيچيده با تعداد زيادي شي در آن داشته باشيد و اگر بخواهيد تغييرات اشيا را در هر فريم ذخيره کنيد ، به حجم بالايي از منابع ذخيره سازي نياز است . بجاي آن ما با استفاده از يکسري فريم کليدي ، فريمهاي مياني را پيش بيني مي کنيم . براي انجام درون يابي فريم کليدي ، بايستي مقدار vertex را در هر فريم کليدي بدانيم و نيز بدانيم هر فريم کليدي در چه زماني ظاهر مي شود . بنابراين بايد براي هر انيميشن چند فايل را بعنوان فريم کليدي ذخيره کنيم . در اين درس ما داده هاي کليدي انيميشن را از يکسري فايل load مي کنيم بنابراين تمام ثابتهاي زمان keyframe درون برنامه قرار داده مي شود ( شما مي توانيد خودتان يک ماژول بنويسيد که انيميشن هاي عمومي تر را نيز مديريت کند . اين ماژول بايد قادر باشد که يک فرمت استاندارد فايل را import کند ، اشيا و texture هاي مربوطه را load نمايد و سپس خودش ساخت انيميشن را بطور اتوماتيک انجام دهد و برنامه اصلي فقط روتين render و يا update را فراخواني کند ) . پس از جمع آوري اطلاعات فريم هاي کليدي ، بايد در هر زمان محاسبه کنيم که چه مدتي از شروع انيميشن گذشته است و بنابراين انيميشن در چه موقعيتي قرار دارد . سپس محاسبه مي کنيم که فريم کليدي قبلي و فريم کليدي بعدي چيست همچنين حساب مي کنيم در چه فاصله زماني از ايندو قرار داريم . سرانجام يک درون يابي نرمال را انجام مي دهيم تا اطلاعات فريم جاري بدست آيد و اين اطلاعات را درون يک شي Mesh مي گذاريم و آنرا رندر مي کنيم . در درسهاي قبلي در مورد load کردن اشيا از يک فايل X صحبت کردم اما در مورد چگونگي گرفتن اطلاعات vertex از يک شي Mesh صحبت نشد . کتابخانه D3DX براي اينکار دو تابع دارد : - تابع D3DXMeshVertexBuffer8GetData : اطلاعات يک شي D3DXMesh را گرفته و در يک آرايه از D3DVERTEX ذخيره مي کند : D3DXMeshVertexBuffer8GetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long - D3DXMeshobj As Unknown = A D3DXMESH object that you want to extract the data from. - Offset As Long = How far into the vertex buffer we want to start reading, 0 is the beginning - Size As Long = Size of the vertex buffer, this will be Len(D3DVERTEX) * Mesh.GetNumVertices - Flags As Long = A combination of the CONST_D3DLOCKFLAGS, leave as 0. - Data As Any = The first element in the array that you want the data to be read into, should be an array of D3DVERTEX vertices - Return Code As Long = Returns D3D_OK for success, or either of D3DERR_INVALIDCALL or E_INVALIDARG for an error - تابع D3DXMeshVertexBuffer8SetData : اطلاعات يک بافر vertex را در يک شي D3DXMesh قرار مي دهد : D3DXMeshVertexBuffer8SetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long - D3DXMeshobj As Unknown = The D3DXMESH object that defines where you want the data to be placed - Offset As Long = How far into the Destination vertex buffer you want to place the data - Size As Long = The Size of the buffer in bytes, this will be Len(D3DVERTEX) * Mesh.GetNumVertices - Flags As Long = A Combination of the CONST_D3DLOCKFLAGS, leave as 0 - Data As Any = The first element in the array of data you want placed in the mesh's vertex buffer - Return Code As Long = D3D_OK for success or D3DERR_INVALIDCALL or E_INVALIDARG for failure عمليات انجام انيميشن فريم کليدي بصورت زير است : - load کردن اشيا از فايلهاي X به درون شي D3DXMesh - استخراج اطلاعات vertex از اين شي - انجام درون يابي بين فريمهاي کليدي - قرار دادن اطلاعات vertex هاي درون يابي در يک شي D3DXMesh فرض مي کنيم که انيميشن ما هميشه از زمان صفر تا زمان n باشد – برحيب ميلي ثانيه – بنابراين مي توانيم از GetTickCount براي توابع زماني خود استفاده کنيم . همچنين يک ساختار را براي هر فريم کليدي بصورت زير تعريف مي کنيم : Private Type KeyFrame شي load شده از يک فايل’ Mesh As D3DXMesh آرايه material براي هر شي’ MatList() As D3DMATERIAL8 آرايه Texture’ TexList() As Direct3DTexture8 تعداد material ها و texture هايي که استفاده مي کنيم’ nMaterials As Long داده هاي vertex براي اين فريم کليدي’ VertexList() As D3DVERTEX موقعيت اين فريم کليدي در انيميشن’ TimeIndex As Long End Type حال بايد تابعي بنويسيم که اطلاعات را از يک فايل X استخراج کرده و درون فريم کليدي قرار دهد : Private Function CreateKeyFrameFromFile(Filename As String, TexturePrefix As String, Time As Long) As KeyFrame نام فايل X براي شي سه بعدي: Filename ’ پوشه اي که اطلاعات texture اين شي در آن قرار دارد : TexturePrefix ’ انديس زمان براي اين فريم کليدي : Time ' Dim I As Long Dim XBuffer As D3DXBuffer Dim TextureFile As String Dim hResult As Long 'خواندن اطلاعات از فايل ورودي به حافظه Set CreateKeyFrameFromFile.Mesh = D3DX.LoadMeshFromX(Filename, D3DXMESH_MANAGED, D3DDevice, Nothing, XBuffer, CreateKeyFrameFromFile.nMaterials)x توليد material ها و texture ها ‘ ReDim CreateKeyFrameFromFile.MatList(CreateKeyFrameFromFile.nMaterials) As D3DMATERIAL8 ReDim CreateKeyFrameFromFile.TexList(CreateKeyFrameFromFile.nMaterials) As Direct3DTexture8 For I = 0 To CreateKeyFrameFromFile.nMaterials - 1 D3DX.BufferGetMaterial XBuffer, I, CreateKeyFrameFromFile.MatList(I)x CreateKeyFrameFromFile.MatList(I).Ambient = CreateKeyFrameFromFile.MatList (I).diffuse TextureFile = D3DX.BufferGetTextureName(XBuffer, I)x If TextureFile <> "" Then Set CreateKeyFrameFromFile.TexList(I) = D3DX.CreateTextureFromFileEx(D3DDevice, TexturePrefix & TextureFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, 0, ByVal 0, ByVal 0)x End If Next I استخراج داده هاي vertex’ ReDim CreateKeyFrameFromFile.VertexList(CreateKeyFrameFromFile.Mesh.GetNumVertices) As D3DVERTEX hResult = D3DXMeshVertexBuffer8GetData(CreateKeyFrameFromFile.Mesh, 0, Len(CreateKeyFrameFromFile.VertexList(0)) * reateKeyFrameFromFile.Mesh.GetNumVertices, 0, CreateKeyFrameFromFile.VertexList(0)) CreateKeyFrameFromFile.TimeIndex = Time End Function در تابع Initialize خطوط زير را براي ساخت فريم هاي کليدي اضافه مي کنيم : nKeyFrames = 4 kfAnimLength = 2500 AnimLastStartAt = GetTickCount()x ReDim kfAnim(nKeyFrames - 1) As KeyFrame kfAnim(0) = CreateKeyFrameFromFile(App.Path & "\frame0.x", App.Path & "\", 0)x kfAnim(1) = CreateKeyFrameFromFile(App.Path & "\frame1.x", App.Path & "\", kfAnimLength * (1 / 3))x kfAnim(2) = CreateKeyFrameFromFile(App.Path & "\frame2.x", App.Path & "\", kfAnimLength * (2 / 3))x kfAnim(3) = CreateKeyFrameFromFile(App.Path & "\frame3.x", App.Path & "\", kfAnimLength)x kfCurrent = CreateKeyFrameFromFile(App.Path & "\frame0.x", App.Path & "\", 0) دقت کنيد که از يک انديس زمان براي ساخت فريم هاي کليدي استفاده شده است . حال بايد کدي براي نمايش دادن انيميشن بنويسيم . ابتدا بايد به روشي تغييرات فريمها را کنترل کنيم : For I = 0 To nKeyFrames - 2 If CurrentTimeIndex >= kfAnim(I).TimeIndex Then PrevFrame = I NextFrame = I + 1 End If Next I سپس بايد با توجه به زمان index دو فريم کليدي و زمان جاري ، پارامتر درون يابي را محاسبه کنيم : sTime = kfAnim(PrevFrame).TimeIndex eTime = kfAnim(NextFrame).TimeIndex cTime = CurrentTimeIndex eTime = eTime - sTime cTime = cTime - sTime sTime = sTime - sTime InterpolateAmount = cTime / eTime سپس بايد بر اساس اين پارامتر عمل درون يابي را روي داده هاي vertex انجام دهيم : For I = 0 To kfCurrent.Mesh.GetNumVertices 'درون يابي مختصاتها D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).X, kfAnim(PrevFrame).VertexList(I).Y, _ kfAnim(PrevFrame).VertexList(I).Z), MakeVector(kfAnim(NextFrame).VertexList(I).X, kfAnim(NextFrame).VertexList(I).Y, _ kfAnim(NextFrame).VertexList(I).Z), InterpolateAmount kfCurrent.VertexList(I).X = vTemp3D.X kfCurrent.VertexList(I).Y = vTemp3D.Y kfCurrent.VertexList(I).Z = vTemp3D.Z 'درون يابي نرمالها D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).nx, kfAnim(PrevFrame).VertexList(I).ny, _ kfAnim(PrevFrame).VertexList(I).nz), MakeVector(kfAnim(NextFrame).VertexList(I).nx, kfAnim(NextFrame).VertexList(I).ny, _ kfAnim(NextFrame).VertexList(I).nz), InterpolateAmount kfCurrent.VertexList(I).nx = vTemp3D.X kfCurrent.VertexList(I).ny = vTemp3D.Y kfCurrent.VertexList(I).nz = vTemp3D.Z 'درون يابي اطلاعات بافت D3DXVec2Lerp vTemp2D, MakeVector2D(kfAnim(PrevFrame).VertexList(I).tu, kfAnim(PrevFrame).VertexList(I).tv), _ MakeVector2D(kfAnim(NextFrame).VertexList(I).tu, kfAnim(NextFrame).VertexList(I).tv), InterpolateAmount kfCurrent.VertexList(I).tu = vTemp2D.X kfCurrent.VertexList(I).tv = vTemp2D.Y Next I حال بايد داده توليد شده را به فرمت Mesh برگردانيم : hResult = D3DXMeshVertexBuffer8SetData(kfCurrent.Mesh, 0, Len(kfCurrent.VertexList(0)) * kfCurrent.Mesh.GetNumVertices, 0, kfCurrent.VertexList(0))x با استفاده از روش فوق مي توانيد هر تعداد فريم کليدي را به انيميشنتان اضافه کنيد . اشکالي که روش فوق دارد اينست که اطلاعات texture براي تمام فريمهاي کليدي جداگانه ذخيره شده است در حاليکه texture در تمام فريمها ثابت است . در درسهاي بعدي از روشي بنام texture pooling استفاده مي کنيم تا تنها يک کپي از texture ها نگهداري کنيم مقدمه : کنترل WinSock نسبت به تمام کنترلهاي اينترنت در سطح پايينتري قرار دارد . اين کنترل امکان ايجاد سرويسهاي شبکه اي مبتني بر پروتکلهاي TCP و UDP را مهيا مي کند . بعبارت ديگر توسط اين کنترل مي توان برنامه هاي کاربردي Client/Server ( سرويس گيرنده / سرويس دهنده ) ايجاد و با استفاده از پروتکل TCP و يا UDP بين آنها ارتباط برقرار نمود . با تنظيم خصوصيات و فراخواني متدهاي اين کنترل مي توانيد به راحتي به يک کامپيوتر راه دور متصل شويد و داده ها را در هر دو جهت جابجا نمائيد . نمونه کاربرهايي که مي توان با اين کنترل ايجاد نمود : Client-server chat ، Mail client ، Mail server ، Proxy Server ، Network Game ، Port Scanner ، پياده سازي الگوريتم هاي موازي و … مباني TCP : پروتکل کنترل اينترنت ( Transfer Control Protocol ) اجازه مي دهد يک اتصال ( Connection ) را از طريق سوکت ( socket ) به يک کامپيوتر راه دور ( Remote Computer ) ساخته و استفاده کنيد . با استفاده از اين اتصال ، هر دو کامپيوتر مي توانند داده ها را بين خودشان انتقال دهند . برقراري ارتباط از طريق TCP همانند صحبت کردن با تلفن است که بايد حتماً اتصالي بين دو کامپيوتر صورت گيرد تا بتوانند با هم ارتباط برقرار کنند . اگر يک برنامه Client مي سازيد بايستي بدانيد که نام يا آدرس IP کامپيوتر Server چيست ( Remote Host IP ) و همچنين از طريق چه پورتي مي توانيد به آن متصل شويد ( Remote Port ) . حال بايستي به آن پورت Connect کنيد . همچنين اگر يک برنامه Server مي سازيد بايستي پورتي را که روي آن به درخواستها گوش مي دهيد مشخص کنيد ( LocalPort ) و سپس به پورت گوش دهيد ( Listen ) . زمانيکه يک کامپيوتر Client تقاضاي يک اتصال را مي دهد Server اين درخواست را Accept مي کند . زمانيکه يک اتصال ساخته مي شود ، هر دو کامپيوتر مي توانند داده را فرستاده و دريافت کنند . مباني UDP : پروتکل ديتاگرام کاربر ( User Datagram Protocol ) پروتکلي بدون اتصال ( Connectionless ) است . برخلاف TCP ، کامپيوترها نياز به برپا کردن يک اتصال ندارند بنابراين يک برنامه مي تواند يک client و يا يک server باشد . برقراري ارتباط در UDP شبيه ارسال نامه از طريق پست است . براي انتقال داده توسط UDP ابتدا بايد Local Port کامپيوتر Client تنظيم گردد . کامپيوتر Server تنها بايستي RemoteHost را برابر آدرس کامپيوتر Client قرار دهد و همچنين Remote Port را همان Local Port کامپيوتر Client قرار دهد . سپس دو کامپيوتر مي توانند داده ها را بين خود جابجا کنند . استفاده از کنترل WinSock : 1 – انتخاب پروتکل: در زمان استفاده از کنترل WinSock اولين کاري که بايد انجام دهيد انتخاب يکي از پروتکلهاي TCP يا UDP است . طبيعت برنامه اي که شما مي سازيد نوع پروتکلي را که بايد استفاده کنيد مشخص مي کند . چند سوال زير به شما کمک مي کند که پروتکل مورد نيازتان را انتخاب کنيد : - آيا برنامه شما در زمانيکه داده فرستاده مي شود يا دريافت مي شود نياز به اطلاعاتي از طرف Server يا Client دارد ؟ اگر چنين است بايستي يک اتصال TCP قبل از ارسال يا دريافت داده ايجاد شود . - آيا داده بسيار بزرگ است ( مثل تصوير يا فايلهاي صوتي ) ؟ زمانيکه يک اتصال TCP ساخته مي شود پروتکل TCP اتصال را باقي نگه مي دارد و درستي ارسال داده تضمين شده است . اين اتصال در هر حال به منابع محاسباتي بيشتري نياز دارد و بنابراين پرهزينه تر است . - آيا داده متناوب ارسال مي شود يا در يک نشست ( Session ) ارسال خواهد شد ؟ براي مثال اگر شما يک برنامه مي سازيد که کامپترهاي مشخصي را در يک زمان خاص از انجام شدن عملياتي مطلع مي کند پروتکل UDP مناسب تر است . پروتکل UDP همچنين براي ارسال مقادير کوچک داده اي مناست تر مي باشد . 2 – تنظيم پروتکل : براي تنظيم پروتکلي که مي خواهيد در برنامه تان از آن استفاده کنيد در زمان طراحي برنامه خاصيت Protocol کنترل WinSock را برابر sckTCPProtocol و يا sckUDPProtocol قرار دهيد . همچنين مي توانيد پروتکل خود را توسط کد زير تنظيم کنيد : WinSock.Protocol=sckTCPProtocol 3 – مشخص کردن نام کامپيوتان : براي اتصال به کامپيوتر راه دور بايستي آدرس IP و يا نام کامپوتر را بدانيد . نام کامپيوتر در Control Panel/Network/Identification موجود است . در صورتيکه مي خواهيد دو برنامه Client و Server خود را روي يک کامپيوتر تست کنيد از آدرس IP 127.0.0.1 براي هر دو استفاده کنيد اما اگر دو برنامه را روي دو کامپيوتر مجزا در شبکه قرار داده ايد با اجراي دستور ipconfig در DOS Prompt مي توانيد آدرس IP کامپيوتر ها را بدست آوريد . 4 – ايجاد اتصال TCP : در زمان ساخت برنامه اي که از پروتکل TCP استفاده مي کند ابتدا بايد تصميم بگيريد که اين برنامه Client است يا Server . براي ساخت يک برنامه Server بايستي روي يک پورت خاص Listen کنيد . زمانيکه Client تقاضاي يک اتصال را مي دهد ، برنامه Server مي تواند آنرا Accept کند و بنابراين اتصال کامل شده است . حال Client و Server مي توانند با هم ارتباط داشته باشند . مراحل زير ساخت يک سرور چت ساده بر مبناي TCP را نشان مي دهد : - از منوي Project گزينه Components را انتخاب کنيد و در ليست Component ها مورد Microsoft WinSock 6.0 را انتخاب کنيد . - يک کنترل WinSock در فرم خود قرار دهيد و نام آنرا tcpserver بگذاريد - دو textbox با نامهاي txtSendData و txtReceiveData و نيز يک دکمه در فرم قرار دهيد . - کد زير را در رويداد Form_Load بنويسيد : Tcpserver.LocalPort=1000 tcpserver.Listen - زمانيکه درخواستي از طرف Client مي آيد رويداد ConnectionRequest اجرا مي شود . در اين رويداد ابتدا بايد چک کنيد که حالت کنترل بسته باشد . اگر چنين نيست اتصال را قبل از پذيرفتن اتصال جديد ببنديد . سپس تقاضا را بر اساس پارامتر requestID مي پذيريم : Private Sub tcpserver_ConnectionRequest(ByVal requestID As Long) If tcpserver.State <> sckClosed Then tcpserver.Close tcpserver.Accept requestID End Sub - حال اتصال بين Client و Server برقرار شده است . کد زير را براي event مربوط به کليک دکمه Send بنويسيد : Tcpserver.SendData txtSendData.text - اگر داده اي از طرف Client بيايد رويداد DataArrival اجرا مي شود . کد زير را براي اين رويداد بنويسيد : Private Sub tcpserver_DataArrival(ByVal bytesTotal As Long) Dim strData As String tcpserver.GetData strData txtReceiveData.Text = strData End Sub - کد زير را براي رويداد Form_Unload بنويسيد : Tcpserver.Close مراحل ساخت يک TCP Client بصورت زير است : - يک کنترل WinSock در فرم قرار دهيد و نام آنرا tcpclient بگذاريد . - دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه با نام sendدر فرم قرار دهيد . - يک دکمه با نام connect در فرم قرار دهيد . - کد زير را براي متد Form_Load بنويسيد : tcpclient.RemoteHost=”yourservername”x tcpclient.RemotePort=1000 - کد زير را براي رويداد کليک شدن دکمه connect بنويسيد : tcpclient.Connect - کد زير را براي رويداد کليک شدن دکمه send بنويسيد : tctclient.SendData txtsend.Text - کد زير را براي رويداد DataArrival بنويسيد : Private Sub tcpclient_DataArrival(ByVal bytesTotal As Long) Dim strData As String tcpclient.GetData strData txtreceive.Text = strData End Sub - کد زير را باري رويداد Form_Unload بنويسيد : Tcpclient.Close کدهاي فوق يک سيستم Client-Server ساده را نشان مي دهد . فايل exe هر دو برنامه را بسازيد و آنها را اجرا کنيد تا بتوانيد سيستم خود را تست کنيد . 5 – پذيرفتن بيش از يک تقاضاي اتصال : Server اي که در بالا ساخته شد تنها مي تواند تقاضاي يک اتصال را بپذيرد . با استفاده از ايجاد يک آرايه از کنترل WinSock مي توان چندين تقاضاي اتصال را پذيرفت . براي اينکار کافي است يک کپي ( instance ) از کنترل بسازيم ( با تنظيم خاصيت Index ) و متد Accept را براي instance جديد بکار ببريم . فرض کنيد يک کنترل WinSock با نام sckServer در فرم داريم که خاصيت Index آنرا صفر قرار داده ايم . همچنين يک متغير intMax از نوع Long تعريف مي کنيم که تعداد اتصالات همزمان به Server را نگه مي دارد . در event مربوط به Form_Load کد زير را بنويسيد : intMax=0 sckServer(0).LocalPort=1000 sckServer(0).Listen هر بار که تقاضاي يک اتصال مي رسد کد ابتدا تست مي کند که مقدار Index چقدر است . اگر مقدار Index صفر باشد متغير intMax يکي افزايش مي يابد و از intMax براي ساخت يک instance جديد از کنترل استفاده مي شود . حال از اين instance براي پذيرفتن تقاضاي اتصال استفاده مي گردد . براي اينکار کد زير را براي رويداد ConnectionRequest بنويسيد : Private Sub sckServer_ConnectionRequest(Index As Integer, ByVal requestID As Long) If Index = 0 Then intmax = intmax + 1 Load sckServer(intmax)x sckServer(intmax).LocalPort = 0 sckServer(Index).Accept requestID End If End Sub 6 – ايجاد اتصال UDP : ساخت يک برنامه UDP ساده تر از برنامه هاي TCP است زيرا پروتکل UDP به اتصال نياز ندارد . در برنامه TCP بالا يک کنترل WinSock بايستي حتماً Listen مي کرد و يک کنترل ديگر يک اتصال را توسط متد Connect ايجاد نمود . در عوض پروتکل UDP نيازي به اتصال ندارد . براي ارسال داده بين دو کنترل WinSock سه مرحله بايستي انجام شود : - پارامتر RemoteHost برابر نام کامپيوتر مقابل است . - پارامتر RemotePort برابر پارامتر LocalPort کامپيوتر مقابل - استفاده از متد Bind براي مشخص کردن LocalPort چون هر دو کامپيوتر از نظر ارتباط مساوي هستند ، اين نوع برنامه ها را Peer-to-Peer گويند . براي نمونه از کد زير براي ساخت يک برنامه chat استفاده مي کنيم : - يک کنترل WinSock در فرم قرار دهيد و نام آنرا udppeerA بگذاريد . - خاصيت Protocol آنرا UDPProtocol قرار دهيد . - دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه در فرم قرار دهيد . - کد زير را براي متد Form_Load بنويسيد : udppeerA.RemoteHost=”nameofpeerB”x udppeerA.RemotePort=1001 udppeerA.Bind 1002 - کد زير را براي event مربوط به کليک دکمه بنويسيد : udppeerA.SendData txtsend.text - کد زير را براي رويداد DataArrival بنويسيد : Dim strData as String udppeerA.GetData strData txtreceive.Text=strData براي ساخت UDP peerB مشابه مراحل بالا عمل کنيد فقط خاصيت RemoteHost آنرا نام کامپيوتر PeerA و خاصيت RemotePort آنرا 1002 و خاصيت Bind آنرا 1001 قرار دهيد . بررسی خواص کنترل WinSock : ByteReceived : مقدار داده دريافت شده ( موجود در بافر receive ) را نشان مي دهد . توسط متد GetData مي توان اين داده را دريافت نمود . LocalHostName : نام ماشين محلي را نشان مي دهد . اين پارامتر فقط خواندني است . LocalIP : آدرس IP ماشين محلي را بصورت يک string برمي گرداند . اين پارامتر فقط خواندني است . LocalPort : براي خواندن و يا تنظيم شماره پورت محلي بکار مي رود . Protocol : براي خواندن و يا تنظيم پروتوکل مورد استفاده توسط کنترل WinSock بکار مي رود . RemoteHost : براي خواندن و يا تنظيم نام يا آدرس IP ماشين راه دور بکار مي رود . RemoteHostIP : آدرس IP ماشين راه دور را برمي گرداند : ۱- براي برنامه هاي Client بعد از زمانيکه يک اتصال توسط متد Connect پذيرفته شد ، اين خاصيت حاوي آدرس IP ماشين راه دور است . ۲ - براي برنامه Server ، بعد از آمدن يک Connection Request اين خاصيت شامل آدرس IP ماشين راه دور است . ۳ - در زمان استفاده از پروتکل UDP بعد از اينکه رويداد Data Arrival رخ داد اين خاصيت حاوي آدرس IP ماشيني است که داده را فرستاده . RemotePort : براي خواندن و يا تنظيم شماره پورت ماشين راه دوري که مي خواهيد به آن متصل شويد بکار مي رود . SocketHandle : مقداري را برمي گرداند که مرتبط با سوکتي است که کنترل WinSock را مديريت مي کند و براي ارتباط با لايه WinSock بکار مي رود . اين پارامتر فقط خواندني است و تنها براي ارسال به API هاي WinSock طراحي شده است . State : وضعيت کنترل WinSock را نشان مي دهد . وضعيتهاي ممکن براي State عبارتند از : ۱ - sckClosed : اتصال بسته است . ۲ - sckOpen : اتصال باز است . ۳ - sckListening : حالت گوش دادن به پورت 4 - sckConnectionPending : معلق شدن اتصال ۵ - sckResolvingHost : تصميم گيري در مورد ميزبان ۶ - sckHostResolved : در مورد ميزبان تصميم گيري شد . ۷ - sckConnecting : حالت برقراري ارتباط ۸ - sckConnected : ارتباط برقرار شد . ۹ - sckClosing : حالت قطع اتصال ۱۰ - sckError : حالت خطا بررسی متدهای کنترل WinSock : متد Accept : تنها براي برنامه هاي TCP Server بکار مي رود . اين متد براي پذيرفتن يک اتصال در زمان مديريت رويداد ConnectionRequest استفاده مي شود . متد Bind : اين پارامتر LocalPort و LocalIP يک اتصال را مشخص مي کند . متد Close : براي بستن يک اتصال TCP و يا بستن يک listening socket بکار مي رود . متد GetData : بلوک جاري داده دريافت شده را گرفته و آنرا در متغيري از نوع Variant ذخيره مي کند . شکل کلي اين متد بصورت زير است : WinSock.GetData data[,type][,maxlen]x که data داده دريافتي است . اگر داده کافي موجود نباشد data برابر empty خواهد بود . type نوع داده دريافتي است که مي تواند مقادير زير باشد : vbByte - vbInteger - vbLong - vbSingle - vbDouble - vbDate - vbBoolean - vbError - vbString - vbArray+vbByte maxlen حداکثر سايز را در زمان دريافت يک byte Array و يا يک string مشخص مي کند . متد Getdata در رويداد Data Arrival استفاده مي شود که اين رويداد يک پارامتر با نام TotalBytes دارد . اگر maxlen اي که شما تعيين کرده ايد کمتر از TotalBytes باشد پيغام هشدار شماره ۱۰۰۴۰ دريافت مي کنيد بدين معني که بايتهاي باقيمانده گم خواهند شد . متد Listen : يک سوکت مي سازد و آنرا در حالت Listen قرار مي دهد . اين متد تنها در اتصالات TCP بکار ميرود . متد PeekData : مشابه GetData است با اين تفاوت که داده را از صف ورودي حذف نمي کند . اين متد تنها براي اتصالات TCP بکار مي رود . متد SendData : براي ارسال داده به کامپيوتر راه دور بکار مي رود . بررسي event هاي کنترل WinSock : رويداد Close : زماني رخ مي دهد که کامپيوتر راه دور اتصال را ببندد . رويداد Connect : بعد از اينکه يک اتصال به Server ايجاد شد روي مي دهد . شکل کلي آن بصورت زير است : Private Sub WinSock_Connect(ErrorOccurred As Boolean)x که پارامتر ErrorOccurred دو مقدار دارد : اگر True باشد يعني اتصال Fail شده است و اگر False باشد يعني اتصال با موفقيت انجام شده است . با رويداد Connect مي توانيد error هايي که در زمان فرايند باز کردن اتصال برگردانده شده را چک کنيد . رويداد ConnectionRequest : زماني رخ مي دهد که يک کامپيوتر راه دور تقاضاي يک اتصال را بدهد . اين رويداد فقط براي برنامه هاي TCP Server بکار مي رود . رويداد DataArrival : زماني رخ مي دهد که داده جديدي بيايد . رويداد Error : زماني رخ مي دهد که يک خطا در فرايند ارتباط رخ دهد ( مثلاً Failed to Connect و يا Failed to Send ) . شکل کلي آن بصورت زير است : Private WinSock_Error(number as Integer,description as String,scode as Long,source as String,helpfile as String,helpcontext as Long,canceldisplay as Boolean)x number شماره کد خطا است . description توضيحي در مورد خطا است . source توصيف منبع خطا canceldisplay : مشخص مي کند آيا پيغام خطاي پيش فرض نشان داده شود يا نه رويداد SendComplete : زماني رخ مي دهد که يک عمل Send تکميل شده باشد . رويداد SendProgress : زماني رخ مي دهد که کنترل شروع به ارسال داده نمايد . شکل کلي آن بصورت زير است : WinSock_SendProgress (bytesSent As Long, bytesRemaining As Long)x که bytesSent تعداد بايتهاي ارسال شده و bytesRemaining تعداد بايتهاي باقيمانده است . نکته ۱ : براي دريافت جدول خطاهاي WinSock با من تماس بگيريد . نکته ۲ : موضوع بعدي : آشنايي با الگوريتم Collision Detection در ساخت انيميشن هاي دوبعدي |
||
|
|
|
|
|
پاسخ دوست یه سوال عجیب؟ چطور ميشه کنترلي نوشت که اگه چند تا از انها رو در فرم گذاشتیم بتونن همديگرو پيدا کنن مثله Raido Button Dim c As Control For Each c In UserControl.Parent.Controls If TypeOf c Is UserControl1 Then MsgBox c.Name ' Put your code here End If Next آشنايي با RAS API و WinInet API – کامل مقدمه ويندوز برای برقراری ارتباط با Internet Service Provide- ISP- شما از طريق مودم و خط تلفن در اتصالات dial-up networking ، از سرويسی خاص به اسم RAS (Remote Access Service) استفاده می کند . اين سرويس دارای يک واسط برنامه نويسی است که RAS API نام دارد . اين واسط شامل مجموعه ای از توابع است که شما می توانيد آنها را در برنامه خود صدا بزنيد . RAS API ابزاری بسيار قدرتمند و قابل انعطاف است همچنين بسيار پيچيده می باشد . خوشبختانه برای استفاده راحتتر ، مايکروسافت تعدادی تابع را در مجموعه ای به اسم WinInet API قرار داده تا بتوان از آنها برای برقراری ارتباط و کنترل اتصال استفاده کرد . آشنايي با WinInet API : WinInet API مجموعه ای از توابع است که امکان ايجاد و توسعه برنامه های اينترنتی را بصورتی ساده ، سريع و کارآمد برای برنامه نويسان مهيا می کند . با استفاده از اين مجموعه توابع شما می توانيد برنامه هايي بنويسيد که از منابع اينترنتی با استفاده از پروتکلهايي چون HTTP و FTP استفاده کنند . همچنين WinInet به شما اجازه می دهد تا بتوانيد ارتباطی dial-up با يک ISP ايجاد نموده و آنرا کنترل کنيد . مزيت اصلی توابع WinInet آينست که شما نيازی به دانستن ساختار پروتکلهای ارتباطی و نيز برنامه نويسی Socket نخواهيد داشت . بعبارت ديگر WinInet يک واسط سطح بالا را برای کار با منابع اينترنتی ارائه می دهد . امکانات Dial-Up موجود در WinInet : تا قبل از ارائه اينترنت اکسپلورر ورژن 4 ، WinInet تنها دارای دو تابع dial-up بود : تابع InternetAttemptConnect : برای بررسی اينکه آيا يک ارتباط به اينترنت وجود دارد يا نه استفاده می شد . اگر هيچ اتصالی به اينترنت وجود نداشت اين برنامه کادر تبادلی dial-up networking را نمايش می داد و کاربر اجازه داشت تا يک اتصال را برای وصل شدن به اينترنت انتخاب کند . تابع InternetCheckConnection : تابع با استفاده از انجام يک دستور ping به url ای که به تابع داده شده ، بررسی می کرد که آيا ارتباطی به اينترنت وجود دارد يا نه . اين دو تابع دارای محدوديتهای فراوانی بودند . برای مثال تابع اول نمی تواند بطور اتوماتيک اتصال به اينترنت را برقرار کند و تابع دوم نيز نمی تواند هيچ اطلاعاتی در مورد نوع ارتباط به ما بدهد . IE نسخه 4 ، تعدادی تابع جديد برای WinInet معرفی کرد که برخی از آنها عبارتند از : تابع InternetGetConnectedState : اطلاعاتی در مورد نوع ارتباط استفاده شده را بيان می کند . برای مثال اين تابع اطلاع می دهد که نوع ارتباط به اينترنت از طريق مودم است يا شبکه LAN و يا از طريق پروکسی . تابع InternetAutodial : اين امکان را فراهم می سازد تا يک ارتباط اينترنتی اتوماتيک از طريق مودم را با استفاده از مدخل اتصال پيش فرض که کاربر آنرا در dial-up networking مشخص کرده ايجاد کنيد . تابع InternetDial : اين تابع کارآمدتر از تابع InternetAutodial است و کادری را نمايش می دهد که کاربر می تواند نوع مدخل مورد نظر خود برای ارتباط تلفنی با اينترنت را انتخاب کند تابع InternetAutodialHangup : برای قطع کردن اتصالی مودمی که از طريق تابع InternetAutodial برقرار شده استفاده می شود تابع InternetHangUp : برای قطع کردن اتصالی مودمی که از طريق تابع InternetDialبرقرار شده استفاده می شود تابع InternetSetDialState : برای تنظيم کردن وضعيت جاری ارتباط اينترنتی استفاده می شود در قسمت بعدی اين سلسه مباحث جزئيات اين توابع را بررسی کرده و نهايتاً برنامه ای کاربردی برای کار با اين توابع در ويژوال بيسيک ارائه خواهم داد . اطلاعات بيشتری در مورد WinInet : در اين بخش ما تنها توابع dial-up موجود در WinInet API را بررسی کرديم اما همانطور که در ابتدا گفته شد WinInet دارای امکانات فراوانی در زمينه کار با اينترنت است . برای آشنايي بيشتر با اين امکانات در زير جداولی ارائه شده که به اختصار امکانات مختلف اين مجموعه تابع را نشان می دهد : توابع Dial-Up : Name Description InternetGetConnectedState Retrieves the current state of the Internet connection InternetAutodial Initiates an unattended dial-up connection InternetAutodialHangup Disconnects a modem connection initiated by InternetDial Initiates a dial-up connection InternetHangUp Disconnects a modem connection initiated by InternetDial InternetGoOnline Prompts the user for permission to initiate a dial-up connection to the given URL InternetSetDialState Sets the current state of the Internet connection توابع عمومی اينترنت : Name Description InternetOpen Initializes the Win32 Internet functions InternetConnect Opens an FTP, Gopher, or HTTP session for a given site InternetCloseHandle Closes a single Internet handle or a subtree of Internet handles InternetErrorDlg Displays a dialog box for the error that is passed to InternetErrorDlg InternetFindNextFile Continues a file search started as a result of a previous call to FtpFindFirstFile or GopherFindFirstFile InternetGetLastResponseInfo Retrieves the last Win32 Internet function error description or server response on the thread calling this function InternetLockRequestFile Allows the user to place a lock on the file being used InternetQueryDataAvailable Queries the amount of data available InternetQueryOption Queries an Internet option on the specified handle InternetReadFile Reads data from a handle opened by the InternetOpenURL, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function InternetReadFileEx Reads data from a handle opened by the InternetOpenURL, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function InternetSetFilePointer Sets a file position for InternetReadFile InternetSetOption Sets an Internet option InternetSetStatusCallback Sets up a callback function that Win32 Internet functions can call as progress is made during an operation InternetStatusCallback Placeholder for the application-defined status callback function InternetTimeFromSystemTime Formats a date and time according to the specified RFC format (as specified in the HTTP version 1.0 specification) InternetTimeToSystemTime Takes an HTTP time/date string and converts it to a SYSTEMTIME structure InternetUnlockRequestFile Unlocks a file that was locked using InternetLockRequestFile InternetWriteFile Writes data to an open Internet file InternetConfirmZoneCrossing Checks for changes between secure and nonsecure URLs توابع URL : Name Description InternetCanonicalizeUrl Canonicalizes a URL, which includes converting unsafe characters and spaces into escape sequences. InternetCombineUrl Combines a base and relative URL into a single URL. The resultant URL will be canonicalized. InternetCrackUrl Cracks a URL into its component parts. InternetCreateUrl Creates a URL from its component parts. InternetOpenUrl Begins reading a complete FTP, Gopher, or HTTP URL. توابع FTP : Name Description FtpCreateDirectory Creates a new directory on the FTP server FtpDeleteFile Deletes a file stored on the FTP server FtpFindFirstFile Searches the specified directory of the given FTP session FtpGetCurrentDirectory Retrieves the current directory for the given FTP session FtpGetFile Retrieves a file from the FTP server and stores it under the specified file name, creating a new local file in the process FtpPutFile Stores a file on the FTP server FtpRemoveDirectory Removes the specified directory on the FTP server FtpRenameFile Renames a file stored on the FTP server FtpSetCurrentDirectory Changes to a different working directory on the FTP server توابع HTTP : Name Description HttpAddRequestHeaders Adds one or more HTTP request headers to the HTTP request handle HttpEndRequest Ends an HTTP request HttpOpenRequest Opens an HTTP request handle HttpQueryInfo Queries for information about an HTTP request HttpSendRequest Sends the specified request to the HTTP server HttpSendRequestEx Sends the specified request to the HTTP server بررسی جزئيات توابع Dial-Up موجود در WinInet : 1 – تابع InternetAutodial : بطور اتوماتيک باعث شماره گيری اتصال پيش فرض اينترنت توسط مودم می شود . اگر اتصال با موفقيت انجام شود تابع مقدار true و در غير اينصورت false بر می گرداند . پارامترهای ورودی تابع : dwFlags : فلگ کنترل کننده عمليات اتصال می باشد و يکی از مقادير زير را می تواند داشته باشد : - INTERNET_AUTODIAL_FORCE_ONLINE - INTERNET_AUTODIAL_FORCE_UNATTENDED dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetAutodial Lib "wininet.dll" (ByVal dwFlags As Long, ByVal dwReserved As Long) As Long 2 – تابع InternetAutodialHangup : باعث قطع کردن يک اتصال dial-up اتوماتيک می شود . اگر قطع اتصال با موفقيت انجام شود تابع مقدار true و در غير اينصورت false برمی گرداند . تابع دارای يک پارامتر ورودی به اسم dwReserved است که رزرو شده بود و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetAutodialHangup Lib "wininet.dll" (ByVal dwReserved As Long) As Long 3 – تابع InternetDial : يک اتصال به اينترنت را با استفاده از يک ارتباط مودم مقداردهی اوليه می کند . پارامترهای ورودی آن عبارتند از : hwndParent : هندل مربوط به پنجره parent lpszConnectoid : نام ارتباط dial-up مورد استفاده dwFlags : فلگ کنترل اتصال که يکی از مقادير زير را می تواند داشته باشد : - INTERNET_AUTODIAL_FORCE_ONLINE - INTERNET_AUTODIAL_FORCE_UNATTENDED - INTERNET_DIAL_UNATTENDED : اتصال به اينترنت از طريق مودم بدون نمايش واسط کاربر lpdwConnection : آدرس داده ای که شامل عدد متناظر با اتصال است . dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetDial Lib "wininet.dll" (ByVal hwndParent As Long, ByVal lpszConnectoid As String, ByVal dwFlags As Long, lpdwConnection As Long, ByVal dwReserved As Long) As Long 4 – تابع InternetGetConnectedState : اين تابع وضعيت اتصال جاری به اينترنت را بر می گرداند . اگر اتصال برقرار باشد تابع مقدار true و در غير اينصورت false برمی گرداند . پارامترهای ورودی تابع عبارتند از : lpdwFlags : توصيف وضعيت اتصال . اين پارامتر يکی از مقادير زير را می تواند داشته باشد : - INTERNET_CONNECTION_MODEM - INTERNET_CONNECTION_LAN - INTERNET_CONNECTION_PROXY - INTERNET_CONNECTION_MODEM_BUSY dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetGetConnectedState Lib "wininet.dll" (ByRef lpdwFlags As Long, ByVal dwReserved As Long) As Long 5 – تابع InternetGoOnline : پيغامی به کاربر برای دادن مجوز برای مقداردهی اوليه اتصال به يک URL را می دهد . اگر اينکار موفقيت آميز باشد مقدار true و در غير اينصورت false برمی گرداند . پارامترهای ورودی تابع عبارتند از : lpszURL : URL وب سايت مورد نظر برای اتصال hwndParent : هندل پنجره parent dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetGoOnline Lib "wininet.dll" (ByVal lpszURL As String, ByVal hwndParent As Long, ByVal dwReserved As Long) As Long 6 – تابع InyernetHangUp : به مودم می گويد که اتصال به اينترنت را قطع کند . پارامترهای اين تابع عبارتند از : dwConnection : شماره مربوط به اتصالی که می خواهيم آنرا قطع کنيم . dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetHangUp Lib "wininet.dll" (ByVal dwConnection As Long, ByVal dwReserved As Long) As Long 7 – تابع InternetSetDialState : تنظيم نمودن وضعيت شماره گيری مودم . اگر تنظيم با موفقيت انجام شود تابع true و در غيراينصورت false برمی گرداند . پارامترهای ورودی تابع عبارتند از : lpszConnectoid : نام اتصال dial-up dwState : وضعيت مربوط به اتصال dial-up . در حال حاضر اين پارامتر تنها مقدار INTERNET_DIALSTATE_DISCONNECTED را می تواند داشته باشد . dwReserved : پارامتری رزرو شده است و بايستی صفر باشد . چگونگی declare کردن تابع : Public Declare Function InternetSetDialState Lib "wininet.dll" (ByVal lpszConnectoid As String, ByVal dwState As Long, ByVal dwReserved As Long) As Long بررسی فلگهای مورد استفاده در توابع dial-up : 1 – فلگهای تابع InternetDial : Public Const INTERNET_DIAL_UNATTENDED = &H8000& '0x8000 Public Const INTERENT_GOONLINE_REFRESH = &H1 '0x00000001 Public Const INTERENT_GOONLINE_MASK = &H1 '0x00000001 2 – فلگهای تابع InternetAutoDial : Public Const INTERNET_AUTODIAL_FORCE_ONLINE = 1 Public Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2 Public Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK = 4 3 – فلگهای تابع InternetGetConnectedState : Public Const INTERNET_CONNECTION_MODEM = 1 Public Const INTERNET_CONNECTION_LAN = 2 Public Const INTERNET_CONNECTION_PROXY = 4 Public Const INTERNET_CONNECTION_MODEM_BUSY = 8 4 - فلگهای مربوط به dial handler اختصاصی : Public Const INTERNET_CUSTOMDIAL_CONNECT = 0 Public Const INTERNET_CUSTOMDIAL_UNATTENDED = 1 Public Const INTERNET_CUSTOMDIAL_DISCONNECT = 2 5 – فلگهای عملياتی پشتيبانی شده برای dial handler اختصاصی : Public Const INTERNET_CUSTOMDIAL_SAFE_FOR_UNATTENDED = 1 Public Const INTERNET_CUSTOMDIAL_WILL_SUPPLY_STATE = 2 Public Const INTERNET_CUSTOMDIAL_CAN_HANGUP = 4 6 - وضعيتهای مربوط به InternetSetDialState : Public Const INTERNET_DIALSTATE_DISCONNECTED = 1 در اين بخش که آخرين بخش از مباحث WinInet API است برنامه ای نمونه برای کار با توابع مودمی اين کتابخانه ارائه خواهيم داد : برای نوشتن برنامه ای که بتوان از طريق آن با استفاده از مودم به اينترنت متصل شد بصورت زير عمل می کنيم : در ابتدا بايستی تابع InternetDial را Declare کنيم : Private Declare Function InternetDial Lib "wininet.dll" Alias "InternetDialA" (ByVal hwndParent As Long, ByVal lpszConnectoid As String, ByVal dwFlags As Long, lpdwConnection As Long, ByVal dwReserved As Long) As Long سپس وضعيت شماره گيری را در متغيری به اسم lOption قرار می دهيم . اين متغير می تواند مقادير زير را داشته باشد : - DF_FORCE_ONLINE - DF_FORCE_UNATTENDED - DF_DIAL_FORCE_PROMPT - DF_DIAL_UNATTENDED حال نام اتصالی را که می خواهيم از آن استفاده شود در متغيری به اسم ConnectionName قرار می دهيم . همچنين دو متغير به اسم ConnectionID و RetVal را از نوع long تعريف می کنيم . حال تابع InternetDial را بصورت زير صدا می کنيم : RetVal = InternetDial(Me.hwnd, ConnectionName, lOption, ConnectionID, 0) اگر RetVal مخالف صفر باشد عمل Dial بدرستی انجام شده است . برای قطع اتصال فوق بايستی از تابع InternetHangUp استفاده کنيم . برای اينکار ابتدا تابع فوق را Declare می کنيم : Private Declare Function InternetHangUp Lib "wininet.dll" (ByVal dwConnection As Long, ByVal dwReserved As Long) As Long سپس اين تابع را بصورت زير فراخوانی می کنيم : RetVal = InternetHangUp(ConnectionID, 0) برای اينکه مودم را مجبور کنيم تا بطور اتوماتيک از اتصال پيش فرض سيستم برای شماره گيری استفاده کند از تابع InternetAutodial استفاده می کنيم . برای اينکار ابتدا تابع را Declare می کنيم : Private Declare Function InternetAutodial Lib "wininet.dll" (ByVal dwFlags As Long, ByVal hwndParent As Long) As Long سپس تابع را بصورت زير فراخوانی می کنيم : RetVal = InternetAutodial(ADF_FORCE_UNATTENDED, Me.hwnd) اگر RetVal مخالف صفر باشد عمل AutoDial بدرستی انجام شده است . برای قطع اتصالی که توسط AutoDial ايجاد شده از تابع InternetAutodialHangup استفاده می کنيم . ابتدا اين تابع را Declare می کنيم : Private Declare Function InternetAutodialHangup Lib "wininet.dll" (ByVal dwReserved As Long) As Long فراخوانی اين تابع بصورت زير است : Call InternetAutodialHangup(0) برای اينکه بفهيم آيا اتصال به اينترنت وجود دارد يا نه از تابع InternetGetConnectedStateEx استفاده می کنيم . برای اينکار ابتدا تابع را Declare می کنيم : Private Declare Function InternetGetConnectedStateEx Lib "wininet.dll" Alias "InternetGetConnectedStateExA" (lpdwFlags As Long, lpszConnectionName As Long, dwNameLen As Long, ByVal dwReserved As Long) As Long سپس تابع را بصورت زير فراخوانی می کنيم : strConnectionName = Space(256) lNameLen = 256 lPtr = StrPtr(strConnectionName) lNameLenPtr = VarPtr(lNameLen) RetVal = InternetGetConnectedStateEx(lConnectionFlags, ByVal lPtr, ByVal lNameLen, 0) که strConnectionName از نوع String و بقيه متغيرها از نوع Long هستند . اگر RetVal مخالف صفر باشد اتصال برقرار است . ثابتهايی که در کدهای فوق استفاده شده عبارتند از : Private Const INTERNET_AUTODIAL_FORCE_ONLINE = 1& Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2& Private Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK = 4& Private Const INTERNET_DIAL_FORCE_PROMPT = &H2000 Private Const INTERNET_DIAL_SHOW_OFFLINE = &H4000 Private Const INTERNET_DIAL_UNATTENDED = &H8000 TAPI چيست ؟ TAPI يا Telephony API يک کتابخانه استاندارد برای کار با مودم و نوشتن برنامه های تلفنی می باشد . برای نمونه می توان از برنامه های Phone Dialer ( شماره گير تلفن ) ، برنامه شبکه سازی تلفنی ( Dialup Networking ) ، برنامه تشخيص پالس مودم برای ضبط اطلاعات وارد شده از طرف کاربران و کاربردهای ديگر در اين زمينه نام برد . اين کتابخانه به شما کمک کمک می کند تا بدون درگير شدن با برنامه نويسی سخت افزار مودم و درايور آن بطور مستقيم بتوانيد برنامه های کاربردیي در اين زمينه بنويسيد . مروری بر Microsoft Telephony : Telephony امکان مجتمع سازی کامپيوترها با دستگاههای ارتباطی و شبکه ها را فراهم نموده است . معمولاً دستگاه ارتباطی يک مودم و خط ارتباطی نيز شبکه PSTN ( شبکه عمومی تلفن سوئيچينگ ) می باشد . برخی از کاربردهای Telephony عبارتند از : ۱ - کنفرانسهای مالتی مديا بصورت Multicast ۲ - VoIP ۳ - مرکز پاسخ گويي اتوماتيک ۴ - تماس تلفنی از طريق کامپيوتر روی شبکه PSTN دياگرام زير معماری Microsoft Telephony را نشان می دهد : برنامه های TAPI : برای نوشتن برنامه های کاربردی با استفاده از TAPI بايستی ابتدا در مورد سطح سرويسی که می خواهيم ارائه دهيم تصميم گيری کنيم . برای مثال برای نوشتن يک برنامه شماره گير تلفن نياز به استفاده کامل از TAPI نيست و می توان از قابليتهای خود ويندوز در اين زمينه استفاده کرد ( Assisted Telephony ) . در بخشهای بعدی در مورد سطوح مختلف سرويس در TAPI بيشتر صحبت خواهم کرد . دومين مطلبی که بايد مورد توجه قرار داد اينست که می خواهيم از TAPI 2.x استفاده کنيم يا از TAPI 3.x . تفاوت ايندو آنست که TAPI ورژن ۲ يک API برمبنای C است در حاليکه ورژن ۳ آن بر مبنای تکنولوژی COM می باشد . در بخشهای بعدی مطالب بيشتری در مورد تفاوتهای اين دو نسخه بيان خواهم کرد . بخشهای اصلی يک برنامه کامل TAPI عبارتند از : ۱ - TAPI Initialization : شامل load کردن TAPI dll ، اتصال به TAPI Server ، مذاکره در مورد ورژن TAPI و برپاسازی سيستم اطلاع رسانی event می باشد . ۲ - Session Control : مقداردهی اوليه ، دريافت و کنترل تماسها ۳ - Device Control : دريافت و تنظيم اطلاعات دستگاه ۴ - Media Control : تشخيص و يا توليد تونها و ارقام ، کنترل stream ۵ - TAPI Shutdown : آزاد سازی منابع مقداردهی اوليه TAPI : عملکرد درست اجزای TAPI نياز به برپاسازی محيط ارتباطی روی کامپيوتر مورد نظر دارد . مراحل اين امر عبارتند از : ۱ - نصب TAPI : زمانيکه سخت افزار و يا نرم افزار برای اولين بار به کامپيوتر اضافه می شود انجام می گيرد . جزئيات کار به سيستم عامل و نرم افزار بستگی دارد . ۲ - مقداردهی ابتدائی : ساخت اشيا و مسيرهای ارتباطی ۳ - مذاکره در مورد ورژن TAPI : برای اطمينان از اينکه اجزای TAPI قادر به تبادل داده ها باشند . ۴ - استخراج اطلاعات منابع : بدست آوردن اطلاعاتی در مورد دستگاهی که می توان از آن در برنامه TAPI مورد نظرمان استفاده نمود . ۵ - Event notification : برپاسازی سيستم اطلاع رسانی event مقداردهی اوليه TAPI در ويژوال بيسيک : از منوی Project گزينه References را انتخاب کرده و از ليست مربوطه مورد Microsoft TAPI 3.0 Type Library را انتخاب کنيد . حال وارد بخش کد نويسی فرمتان شويد و متغير objTAPI را بصورت زير تعريف کنيد : Dim objTapi As TAPI سپس در بخش مربوط به Form Load شی objTAPI را بصورت زير ايجاد می کنيم : Set objTapi = New TAPI همانطور که در بخشهای قبلی گفته شد ، قبل از فراخوانی هر تابع TAPI ابتدا بايستی آنرا مقداردهی اوليه کنيم . برای مقداردهی اوليه کردن شی TAPI عبارت زير را بنويسيد : Call objTapi.Initialize انتخاب يک آدرس : کد زير نشان می دهد که چگونه می توان با استفاده از شی TAPI در ويژوال بيسيک منابع تلفنی در دسترس را برای يک آدرس که بتواند يک مجموعه مشخص از نيازها را مديريت کند ، بررسی کرد . توجه داشته باشيد که قبل از انجام اين کار بايستی عمل مقداردهی اوليه TAPI را که در بخش قبل ررسی شد ، انجام دهيد . نکته : در کد زير عمل error checking انجام نگرفته است و برای استفاده از کد زير در برنامه های واقعی بايستی بخش بررسی خطا را به آن اضافه کنيد . ۱ - تعريف يک شی آدرس و يک شی مجموعه آدرس : Dim gobjAddress As ITAddress Dim objCollAddresses As ITCollection ۲ - تنظيم شی objCollAddress بعنوان يک مجموعه آدرس از شی objTapi : Set objCollAddresses = objTapi.Addresses ۳ - پيدا کردن آدرسی که بتواند از واسط مورد نظر ما پشتيبانی کند : bFound = False For indexAddr = 1 To objCollAddresses.Count Set objCrtAddress = objCollAddresses.Item(indexAddr)x Set objMediaSupport = objCrtAddress Set objAddressCapabilities = objCrtAddress If objMediaSupport.QueryMediaType( nSelectedType ) x bFound = True End If Set objAddressCapabilities = Nothing Set objMediaSupport = Nothing Set objCrtAddress = Nothing If bFound = True Then Exit For Next indexAddr در صورتيکه آدرس مورد نظزر پيدا شود برنامه از حلقه خارج شده و gobjAddress يک آدرس قابل استفاده خواهد بود : Set gobjAddress = objcollAddresses.Item(indexAddr)x انجام Event Handling در TAPI : کد زير شامل يک event handler ساده برای TAPI ، رجيستر کردن واسط event ، تنظيم فيلتر event و رجيستر کردن تمام فراخوانيهای دادن اخطار است . هدف اصلی از اين کد اينست که مطمئن شويم بخشی از TAPI که event ها را دريافت می کند پردازشی را قبل از انتقال به بخشهای ديگر انجام دهد . تعاريفها : Dim WithEvents gobjTapiWithEvents As TAPI Attribute gobjTapiWithEvents.VB_VarHelpID = -1 Dim glRegistrationToken As Long Const TAPI3_CALL_EVENTS =TE_CALLMEDIA Or TE_CALLNOTIFICATION Or TE_CALLSTATE تنظيم eventfilter بصورتيکه تمام event های تعريف شده برای TAPI را بپذيرد : objTapi.EventFilter = TAPI3_CALL_EVENTS رجيستر کردن event ها : Set gobjTapiWithEvents = objTapi Dim fOwner As Boolean, fMonitor As Boolean Dim lMediaTypes As Long, lCallbackInstance As Long fOwner = True fOwner = True fMonitor = False lMediaTypes = TAPIMEDIATYPE_AUDIO lCallbackInstance = 1 glRegistrationToken = gobjTapi.RegisterCallNotifications(gobjAddress,fMonitor, fOwner,lMediaTypes,lCallbackInstance)x انتخاب يک ترمينال : + قبل از اينکه يک ترمينال را برای برقراری ارتباط انتخاب کنيد بايستی TAPI Initialization و عمل انتخاب آدرس را انجام داده باشيد . ابتدا يک متغير از نوع ITBasicCallControl ( واسط کنترل تماس ) تعريف می کنيم : Dim objCallControl As ITBasicCallControl Set objCallControl = gobjReceivedCallInfo سپس يک متغير از نوع ITTerminalSupport ( کوئری از شی آدرس ) تعريف می کنيم : Dim objTerminalSupport As ITTerminalSupport Set objTerminalSupport = gobjAddress سپس متغير ترمينال را تعريف کرده و توسط شی objTerminalSupport يک ترمينال را برای آن استخراج می کنيم : Dim objTerminal As ITTerminal Set objTerminal = objTerminalSupport.GetDefaultStaticTerminal(lMediaType, dir)x در اينجا ديگر نيازی به شی objTerminalSupport نيست بنابراين آنرا آزاد می کنيم : Set objTerminalSupport = Nothing سپس نياز به تعريف شی objStreamControl برای کنترل ترمينال است : Dim objStreamControl As ITStreamControl Set objStreamControl = objCallControl در صورتيکه اين شی ايجاد شود ، به ازای استريم های موجود در ITCollection امکان ايجاد ترمينال در يک حلقه for بررسی می شود و ترمينال مناسب انتخاب می گردد : If Not (objStreamControl Is Nothing) Then Dim objITCollStreams As ITCollection Set objITCollStreams = objStreamControl.Streams Dim nIndex As Long, objCrtStream As ITStream For nIndex = 1 To objITCollStreams.Count Set objCrtStream = objITCollStreams.Item(nIndex)x If objCrtStream.MediaType = lMediaType Then If objCrtStream.Direction = dir Then Call objCrtStream.SelectTerminal(objTerminal)x End If End If Set objCrtStream = Nothing Next nIndex Set objITCollStreams = Nothing Set objStreamControl = Nothing End If ايجاد يک تماس ( Make a Call ) : + قبل از اين بخش بايستی مراحل TAPI Initialization و عمل انتخاب آدرس انجام شده باشد . اين بخش برای ايجاد يک شی تماس ، بررسی و مشخص کردن استريمی که با اين تماس در ارتباط است ، انتخاب و ايجاد ترمينالهای مناسب و کامل کردن ارتباط استفاده می شود . قبل TAPI Initialization و عمل انتخاب آدرس و انتخاب ترمينال انجام شده باشد . در ابتدا با استفاده از متد CreateCall يک شی تماس ساخته می شود : Set gobjCall = gobjOrigAddress.CreateCall(strDestAddress, nSelectedType,lMediaTypes)x سپس در اينجا بايستی کدی که در بخش اول اين درس برای انتخاب ترمينال نوشته شد آورده شود : } Select Terminal Code { سپس بايستی دستور Connect اجرا شود : gobjCall.Connect (False)x False بدين معناست که ارتباط بصورت آسنکرون برقرار می شود . دريافت يک تماس : کد زير برای يافتن و يا ايجاد يک ترمينال مناسب برای دريافت يک تماس بکار می رود . بايستی توجه داشته باشيد که قبل از اجرای کد زير بايستی مراحل مقداردهی اوليه ، انتخاب يک آدرس و رجيسر کردن event ها را انجام دهيد . همچنين در کد زير بايستی مرحله انتخاب ترمينال را نيز انجام دهيد . توجه داشته باشيد که در کد زير متغير pEvent يک اشاره گر برای واسط ITCallNotificationEvent است که توسط TAPI به event Handler داده می شود : If TapiEvent = TE_CALLNOTIFICATION Then Dim objCallNotificationEvent As ITCallNotificationEvent Set objCallNotificationEvent = pEvent Dim gobjReceivedCallInfo As ITCallInfo Set gobjReceivedCallInfo = objCallNotificationEvent.Call Dim objCallControl As ITBasicCallControl Set objCallControl = gobjReceivedCallInfo objCallControl.Answer End If گرفتن اطلاعات ورودی از کيبرد مقدمه Direct Input 8 همانطور که از نامش مشخص است به شما اجازه می دهد که بتوانيد برنامه هايي بنويسيد که توسط هر نوع دستگاه ورودی کنترل شود . Direct Input 8 دارای چندين مزيت نسبت به استفاده از کنترلهای ورودی خود ويژوال بيسيک دارد – کنترلهايي مثل Form_KeyUp, Form_KeyDown, Form_MouseMove - و همچنين قابليت کنترل بيشتری نسبت به توابع استاندارد Win32 از قبيل GetCursorPos, GetKeyState دارد . Direct Input 8 سريعتر ، کاراتر و قدرتمند تر بوده و برای ساخت بازيها طراحی شده بنابراين باعث کندی برنامه ها نخواهد شد . چگونگی کار با Direct Input 8 برای گرفتن ورودی از کيبرد دو روش برای استفاده از کيبرد در DirectX8 وجود دارد : روش polling و روش event-based که هر دو دارای مزايا و معايبی هستند . بطور کلی در اغلب طراحيها از روش event-based استفاده می شود زيرا کار با آن راحت تر اسن . در اين روش هر پيغام فرستاده شده ازطرف دستگاه ورودی log می شود و برنامه نيازی به هيچگونه پردازشی بمنظور منتظر ماندن برای يک پيغام از طرف ورودی ندارد ، بنابر اين کاراتر است . در روش polling کنترل کمی دقيقتر و راحتر است . اگر در مورد برنامه نويسی بر مبنای polling و بر مبنای event اطلاعات کافی نداريد می توانيد از منابع موجود در سايتهايي چون Gamasutra و GameDev استفاده کنيد . روش Polling مراحل اين روش عبارتند از : 1 – تعريفات Declerations : يک فرم ايجاد کرده و يک TextBox به نام txtOutput با خصوصيات Multiline ، Locked و Vertical Scroll Bar در آن قرار دهيد . کدهای زير را در بخش کدنويسی اين فرم بنويسيد : Private Const UsePollingMethod As Boolean = True Private Const UseEventMethod As Boolean = False نکته مهم اينست که تنها يکی از دو ثابت فوق بايستی True باشد . Private bRunning As Boolean اين متغير برای polling استفاده می شود Private DX As DirectX8 Private DI As DirectInput8 تعريف شی اصلی DirectX و شی DirectInput Private DIDevice As DirectInputDevice8 Private DIState As DIKEYBOARDSTATE اين دو شی برای دسترسی به دستگاه ورودی ( کيبرد ) استفاده می شوند Private KeyState(0 To 255) As Boolean آرايه ای برای تشخيص فشرده شدن کليد Private Const BufferSize As Long = 10 سايز بافر نگهدارنده event ها . در روش event-based اين مقدار برابر يک و در روش polling برابر 10 تا 20 است ( بسته به سرعت حلقه بازی ) Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)x تابع Sleep برای متوقف کردن حلقه polling در صورت بالا بودن نرخ ورودی 2- مقدار دهی اوليه Initialisation : اين بخش سه مرحله دارد : در مرحله اول اشيا و Device ها ساخته می شوند . در مرحله دوم تنظيمات مربوط به Device انجام می شود . در مرحله سوم به Device می گوئيم که می خواهيم شروع به استفاده از آن کنيم . در Form_Load کدهای زير را بنويسيد : Me.Show Dim I As Long Dim DevProp As DIPROPLONG Dim DevInfo As DirectInputDeviceInstance8 Dim pBuffer(0 To BufferSize) As DIDEVICEOBJECTDATA If UsePollingMethod And UseEventMethod Then MsgBox "You must select only one of the constants before running"x Unload Me End End If If UsePollingMethod Then txtOutput.Text = "Using Polling Method" & vbCrLf If UseEventMethod Then txtOutput.Text = "Using Event Based Method" & vbCrLf مقداردهی اوليه روش انتخاب شده Set DX = New DirectX8 Set DI = DX.DirectInputCreate Set DIDevice = DI.CreateDevice("GUID_SysKeyboard")x DIDevice.SetCommonDataFormat DIFORMAT_KEYBOARD DIDevice.SetCooperativeLevel frmMain.hWnd, DISCL_BACKGROUND Or ISCL_NONEXCLUSIVE برپاسازی بافر DevProp.lHow = DIPH_DEVICE DevProp.lData = BufferSize DIDevice.SetProperty DIPROP_BUFFERSIZE, DevProp به دايرکت ايکس می گوئيم که می خواهيم از دستگاه ورودی استفاده کنيم DIDevice.Acquire استخراج اطلاعاتی در مورد دستگاه ورودی Set DevInfo = DIDevice.GetDeviceInfo()x txtOutput.Text = txtOutput.Text & "Product Name: " & DevInfo.GetProductName & vbCrLf txtOutput.Text = txtOutput.Text & "Device Type: " & DevInfo.GetDevType & vbCrLf txtOutput.Text = txtOutput.Text & "GUID: " & DevInfo.GetGuidInstance & vbCrLf در صورتی که بخواهيم به برنامه خاتمه بدهيم کدهای زير را می نويسيم DIDevice.Unacquire Set DIDevice = Nothing Set DI = Nothing Set DX = Nothing Unload Me End 3 – گرفتن ورودی از کيبرد : در اين بخش فرض کنيد بخواهيم يک بازی را در يک حلقه Do-Loop شبيه سازی کنيم . در اين حلقه هر بار فشرده شدن کليدهای کيبرد را چک می کنيم : If Not Err.Number Then bRunning = True Do While bRunning دريافت اطلاعات شامل خواندن وضعيت کيبرد ، خواندن اطلاعات بافر و سپس خطا DIDevice.GetDeviceStateKeyboard DIState DIDevice.GetDeviceData pBuffer, DIGDD_DEFAULT If Err.Number = DI_BUFFEROVERFLOW Then Msgbox(“BUFFER OVERFLOW (Compensating)...")x GoTo ENDOFLOOP: End If ‘بررسی فشرده شدن کليدها For I = 0 To 255 If DIState.Key(I) = 128 And (Not KeyState(I) = True) Then txtOutput.Text = txtOutput.Text & "{ DOWN } " & KeyNames(CInt(I))& vbCrLf txtOutput.SelStart = Len(txtOutput.Text)x KeyState(I) = True End If Next I ‘بررسی رها شدن کليد For I = 0 To BufferSize If KeyState(pBuffer(I).lOfs) = True And pBuffer(I).lData = 0 Then KeyState(pBuffer(I).lOfs) = False txtOutput.Text = txtOutput.Text & "{ UP } " & KeyNames(CInt(pBuffer(I).lOfs)) & vbCrLf txtOutput.SelStart = Len(txtOutput.Text)x End If Next I Sleep (50)x DoEvents ENDOFLOOP: Loop در کد فوق يک تابع KeyName وجود دارد که نام کليد فشارداده شده را بر می گرداند . بخشی از اين تابع را در زير می بينيد : Function KeyNames(iNum As Integer) As String Dim aKeys(0 To 255) As String aKeys(1) = "DIK_ESCAPE" aKeys(2) = "DIK_1 On main keyboard"x aKeys(3) = "DIK_2 On main keyboard"x aKeys(4) = "DIK_3 On main keyboard"x aKeys(5) = "DIK_4 On main keyboard"x aKeys(6) = "DIK_5 On main keyboard"x aKeys(7) = "DIK_6 On main keyboard"x aKeys(8) = "DIK_7 On main keyboard"x aKeys(9) = "DIK_8 On main keyboard"x aKeys(10) = "DIK_9 On main keyboard"x aKeys(11) = "DIK_0 On main keyboard"x aKeys(12) = "DIK_MINUS On main keyboard"x aKeys(13) = "DIK_EQUALS On main keyboard"x aKeys(14) = "DIK_BACK BACKSPACE"x aKeys(15) = "DIK_TAB"x aKeys(16) = "DIK_Q"x aKeys(17) = "DIK_W"x aKeys(18) = "DIK_E"x aKeys(19) = "DIK_R"x aKeys(20) = "DIK_T"x . . . KeyNames = aKeys(iNum)x End Function موضوع : کنترل کيبرد با روش Event-Based مقداردهی اوليه و مفاهيم اصلی در روش Event-Based مشابه روش Polling است و تنها بايستی ساختار بخش جمع آوری داده و حلقه پردازشی را تغيير دهيم . مراحل کار با روش Event-Based بصورت زير می باشد : ۱ - تعاريف و مقداردهی اوليه : در بخش تعاريف دو تعريف جديد بصورت زير داريم : Dim hEvent As Long Implements DirectXEvent8 hEvent يک پارامتر هندل برای يک می باشد . نکته : زمانی که کليدی فشرده يا رها می شود ، DirectX اين امر با فراخوانی تابعی به اسم DirectXEvent8_DXCallback به برنامه شما اطلاع می دهد . ( اين نوع توابع را Call Back Function گويند ) . اين تابع به برنامه شما می گويد که يک رويداد اتفق افتاده است و بايستی بافرها را چک کند . تنها تغييری که در بخش مقداردهی اوليه نياز است ، برپاسازی يک event می باشد : If UseEventMethod Then hEvent = DX.CreateEvent(frmMain)x DIDevice.SetEventNotification hEvent End If در انتهای برنامه نيز کد زير را برای از بين بردن event اضافه کنيد : If hEvent <> 0 Then DX.DestroyEvent hEvent ۲ - استفاده از event : برای اين بخش کدهايي را در داخل تابع DirectXEvent8_DXCallback می نويسيم : Private Sub DirectXEvent8_DXCallback(ByVal eventid As Long)x 'متغيرهای موردنياز Dim I As Long Dim pBuffer(0 To BufferSize) As DIDEVICEOBJECTDATA If eventid = hEvent Then If DIDevice Is Nothing Then Exit Sub 'درصورت رخ دادن event داده را از کيبرد می گيريم DIDevice.GetDeviceStateKeyboard DIState DIDevice.GetDeviceData pBuffer, DIGDD_DEFAULT 'چک کردن تمام کليدها برای اينکه متوجه شويم چه اتفاقی افتاده است For I = 0 To 255 'عدد ۱۲۸ نشان دهنده key_down event است . If DIState.Key(I) = 128 Then If pBuffer(0).lData = 128 Then txtOutput.Text = txtOutput.Text & "{ DOWN } " & KeyNames(CInt(I)) & vbCrLf End If End If 'کد فوق برای بررسی فشرده شدن يک کليد بود . کد زير رها شدن کليد را بررسی می کند If (pBuffer(0).lData = 0 And pBuffer(0).lOfs = I) Then txtOutput.Text = txtOutput.Text & "{ UP }" & KeyNames(CInt(I)) & vbCrLf End If txtOutput.SelStart = Len(txtOutput.Text)x Next I End If End Sub موضوع : کنترل ماوس با DirectX Input مقدمه : برای استفاده از ماوس در برنامه های مالتی مديا و بازيها همانند کی برد می توانيم از امکانات دايرکت ايکس استفاده کنيم . روش کنترل ماوس توسط DirectX Input بسيار ساده بوده و مشابه کنترل کيبرد می باشد بنابراين درصورتی که دو درس گذشته را نخوانده اين پيشنهاد می کنم ابتدا آنها را مطالعه کنيد . برپاسازی Device : علاوه بر متغيرهايي که در بخش کنترل کيبرد تعريف شد بايستی متغيرهای جديد زير را نيز در ابتدای برنامه تان تعريف کنيد : Private Const mSpeed As Single = 2 Private Const BufferSize As Long = 10 Private mPosition As Point mSpeed مقدار سرعت حرکت کرسر ماوس را مشخص می کند . BufferSize سايز بافر DI می باشد . mPosition موقعيت جاری کرسر ماوس را نشان می دهد . در مرحله بعدی بايستی مقداردهي های اوليه لازم را انجام دهيد : Set DIDevice = DI.CreateDevice("guid_SysMouse")x Call DIDevice.SetCommonDataFormat(DIFORMAT_MOUSE)x Call DIDevice.SetCooperativeLevel(frmMain.hWnd, DISCL_FOREGROUND Or DISCL_EXCLUSIVE)x تفاوت عمده کدهای فوق با کدهای مقداردهی اوليه در بخش کی برد آنست که cooperativelevel تغيير کرده است . در اينجا گفته شده که ما می خواهيم از ماوس بصورت انحصاری در برنامه استفاده کنيم . اين حالت برای برنامه های window-base مناسب نيست و بهترست از آن در بازيهايي که بصورت full screan هستند استفاده کنيد . خواندن ورودی از ماوس : در اين بخش می توانيد هم از روش polling و هم event-based استفاده کنيد . نکته مهمی که در اينجا وجود دارد آنست که Direct Input فقط حرکت داده شدن ماوس و کليک شدن يک دکمه را به شما اطلاع می دهد و برای تشخيص حالتهای double click و single click خودتان بايستی کد بنويسيد برای مثال اگر فاصله زمانی بين دو کليک کمتر از ۴۰ ميلی ثانيه باشد آنگاه اين يک double click بوده است . کد زير حرکت داده شدن ماوس و کليک يکی از سه دکمه آنرا اطلاع می دهد : Dim DevData(1 To BufferSize) As DIDEVICEOBJECTDATA Dim nEvents As Long Dim I As Long nEvents = DIDevice.GetDeviceData(DevData, DIGDD_DEFAULT)x For I = 1 To nEvents Select Case DevData(I).lOfs Case DIMOFS_X mPosition.x = mPosition.x + (DevData(I).lData * mSpeed)x If mPosition.x < 0 Then mPosition.x = 0 If mPosition.x > frmMain.ScaleWidth Then mPosition.x = frmMain.ScaleWidth imgCursor.Top = mPosition.y imgCursor.Left = mPosition.x lablel(1).Caption = "Mouse Coordinates: [" & mPosition.x & ", " & mPosition.y & "]"x Case DIMOFS_Y mPosition.y = mPosition.y + (DevData(I).lData * mSpeed)x If mPosition.y < 0 Then mPosition.y = 0 If mPosition.y > frmMain.ScaleHeight Then mPosition.y = frmMain.ScaleHeight imgCursor.Top = mPosition.y imgCursor.Left = mPosition.x lablel(1).Caption = "Mouse Coordinates: [" & mPosition.x & ", " & mPosition.y & "]"x Case DIMOFS_BUTTON0 label(2).Caption = "Button 0 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x Case DIMOFS_BUTTON1 label(3).Caption = "Button 1 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x Case DIMOFS_BUTTON2 label(4).Caption = "Button 2 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x Case DIMOFS_BUTTON3 label(5).Caption = "Button 3 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x End Select Next I برای استفاده از کد فوق در روش Polling ، بايستی آنرا در يک حلقه Do while-Loop قرار دهيد . برای استفاده از کد فوق در روش Event-Based ، بايستی آنرا درون روتين DirectXEvent8_DXCallback قرار دهيد آشنايي با کتابخانه Windows Packet Capture معرفی : کتابخانه WinPcap يک معماری برای استخراج Packet های TCP/IP و آناليز شبکه در محيطهای ۳۲ بيتی ويندوز می باشد . اين کتابخانه شامل سه بخش است : ۱ - يک فيلتر Packet در سطح هسته سيستم عامل ( Kernel ) ۲ - يک کتابخانه dll سطح پايين ( low-level ) با نام packet.dll ۳ - يک کتابخانه مستقل از سيستم عامل و سطح بالا ( high-level ) با نام wpcap.dll فيلتر packet يک درايور دستگاه ( device driver ) است که به ويندوزهای ۹۵ ، ۹۸ ، ME ، NT و ۲۰۰۰ قابليت استخراج و capture کردن و نيز ارسال داده خام ( raw data ) از يک کارت شبکه را می دهد . همچنين اين امکان را دارد که packet های capture شده را در يک بافر ذخيره کند و يا آنها را فيلتر نمايد . packet.dll يک API است که بمنظور دسترسی مستقيم به عملکرد درايور packet استفاده می شود . بنابراين packet.dll يک واسط برنامه نويسی مستقل از سیستم عامل های مايکروسافت را مهيا می کند . Wpcap.dll مجموعه ای از ابزارهای سطح بالای اصلی برای capture را مهيا می کند که اين توابع با کتابخانه libpcap ( کتابخانه capture در سيستم عامل UNIX ) سازگار می باشند . اين توابع اجازه capture کردن packet ها را با روشی مستقل از سخت افزار شبکه و مستقل از سيستم عامل مهيا می کنند موضوع : پخش افکتهاي صوتی در برنامه هاي مالتي مديا مقدمه : در سلسله مباحث DirectXAudio شما تکنيکهاي لازم براي اضافه کردن موزيک و افکتهاي صوتي سريع و ديناميک را به بازيها و برنامه هاي مالتي مديا خواهيد آموخت . DirectXAudio جايگزيني براي بخشهاي DirectSound ، DirectSound3D و DirectMusic موجود در DirectX 7 مي باشد و داراي امکانات بهتر و سريعتری بوده و برنامه نويسي آن نيز ساده تر است . در اولين درس از DirectXAudio چگونگي پخش افکتهاي صوتي را در برنامه هايتان خواهيد آموخت . Initial کردن DirectSound : DirectSound اولين مبحثي است که آنرا توضيح خواهم داد . گرچه DirectXAudio يک نام عمومي براي امکانات صوتي DirectX8 مي باشد اما بين Sound و Music تفاوت وجود دارد . DirectSound با پخش افکتهاي صوتي ارتباط دارد . DirectSound همانند Direct3D از يکسري device سخت افزاري و نرم افزاري استفاده مي کند و افکتهاي صوتي در يکسري بافر ذخيره مي شوند . اولين قدم براي برپاسازي DirectSound ، اضافه کردن کتابخانه DirectX8 به پروژه تان مي باشد . قدم بعدي تعريف متغيرها و object هاي موردنياز است . براي استفاده از DirectSound به متغيرهاي زير نياز داريم : Private DX As DirectX8 Private DS As DirectSound8 Private DSBuffer As DirectSoundSecondaryBuffer8 Private DSEnum As DirectSoundEnum8 Private bLoaded As Boolean DirectX شي کنترل کننده مرکزي است . DirectSound8 واسط مراقب براي تمام interface هاي پخش صدا است . DirectSoundSecondaryBuffer8 داده audio واقعي را براي پخش ذخيره مي کند . DirectSoundEnum8 اجازه مي دهد که اطلاعاتي را در مورد device هاي سخت افزاري/نرم افزاري استخراج کنيد و متغير bLoaded يک flag وضعيت مي باشد . حال در برنامه بايد ليست تمام device هاي در دسترس را مشخص کنيم . ( اين امر کاملاً امکان پذير است که يک کامپيوتر بيش از يک device براي DirectSound داشته باشد ) : Private Sub Form_Load()x bLoaded = False Dim I As Long Set DX = New DirectX8 Set DSEnum = DX.GetDSEnum For I = 1 To DSEnum.GetCount MsgBox(DSEnum.GetDescription(I))x Next I End Sub فرض کنيم که يکي از device هاي شناخته شده را انتخاب کرديم . حال بايستي device را واقعاً برپا کنيم : If bLoaded Then Set DSBuffer = Nothing Set DS = Nothing Set DX = Nothing End If Dim DSBDesc As DSBUFFERDESC Set DX = New DirectX8 Set DS = DX.DirectSoundCreate(DSEnum.GetGuid(devicenumber))x DS.SetCooperativeLevel frmMain.hWnd, DSSCL_NORMAL متغير devicenumber شماره device اي است که شما مي خواهيد با آن کار کنيد . DSBDesc فايل صوتي شما را توصيف مي کند . |
||
|
|
|
|
|
قرار دادن متن به صورت عمودی در یک کنترل Text Box ابتدا یک کنترل Picture Box به فرم اضافه کنید. که به طور پیش فرض Picture1 ایجاد می شود. خصوصیت AuotRedraw کنترل مذبور را به True تنظیم کنید. بعد یک کنترل Text Box به فرم روی کنترل Picture Box اضافه کنید. Text1 به وجود می اید و سپس خصوصیت MultiLine این را به True تنظیم کنید. بعد این کدها را در فرمتون کپی کنید Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Const WM_USER = &H400 Const EM_GETLINECOUNT = &HBA Dim numlines As Long Private Sub Form_Load() Dim ht As Integer Text1.Left = 0 Text1.Height = Picture1.Width - 400 Text1.Width = Picture1.TextHeight("A") Text1.Top = (Picture1.Height - Text1.Height) / 2 + 170 Text1.Visible = True numlines = 1 End Sub Private Sub Text1_Change() Dim ret As Long Dim ht As Long ret = SendMessage(Text1.hwnd, EM_GETLINECOUNT, 0, ByVal 0&) If ret <> numlines Then ht = Picture1.TextHeight("A") Text1.Top = (Picture1.Height - Text1.Height) / 2 + 170 numlines = ret SendKeys "{PGUP}", True Text1.SelStart = Len(Text1) End If End Sub پاسخ اقا محمد اسماعیل حسنی که خواسته بودند نحوه ذخیره متن داخل یک TextBox رو توضیح بدم یک فرم خالی درست کنید و فقط یک TextBox روی ان قرار دهید و سپس این کدها رو داخل فرم کپی کنید Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Private Sub Form_Load() Dim buf As String buf = Space(255) GetPrivateProfileString "form1", "textbox1", "10", buf, 255, "c:\testini.ini" Text1.Text = buf End Sub Private Sub Form_Unload(Cancel As Integer) WritePrivateProfileString "form1", "textbox1", Text1.Text, "c:\testini.ini" End Sub و ضمنأ من ویژوال بیسیک فارسی رو با خود ویژوال بیسیک 6 و C++6 نوشتم. توجه : یاهو مسنجر من خرابه و تا اطلاع ثانوی نمیتوانم با هیچ کس چت کنم.ببخشید. اقا کمال برنامه ای رو که خواسته بودید براتون میل کردم. برنامه درخواستی اقای نفوذگر تنها(Hacker alone) فقط یک فرم درست کن و این کدها رو توش کپی کن البته شاید... فعلا چون نمی خواستم منتظرت نگذارم اینو گذاشتم قبلا یه همچین برنامه ای داشتم ولی باورت میشه شاید بیش از نیم ساعت تو هاردم گشتم ولی پیداش نکردم اگه پیداش کردم حتما خبرت میکنم و میزارمش یا شاید هم اگه وقت کردم نوشتمش Private Const SHFD_CAPACITY_DEFAULT = 0 Private Const SHFD_CAPACITY_360 = 3 Private Const SHFD_CAPACITY_720 = 5 Private Const SHFD_FORMAT_QUICK = 0 Private Const SHFD_FORMAT_FULL = 1 Private Declare Function SHFormatDrive Lib "shell32" (ByVal hwndOwner As Long, ByVal iDrive As Long, ByVal iCapacity As Long, ByVal iFormatType As Long) As Long Private Sub Form_Load() SHFormatDrive Me.hWnd, 2, SHFD_CAPACITY_DEFAULT, SHFD_FORMAT_QUICK End Sub اقا حامد گفته بودن من چه کتابهایی برای یادگیری VB میخونم یاید بگم: اموزش ویژوال بیسیک در 21 روز اموزش C++ در 21 روز ( که تا حدودی مرتبط است ) اموزش برنامه نویسی تحت اینترنت با ویژوال بیسیک 211 نکته برای برنامه نویسان ویژوال بیسیک (چکیده MSDN ) 500 تابع کاربردی API و ویژوال بیسیک توسط داییم که استاد کامپیوتر به من فقط معرفی شد که من خودم با پشتکارم یاد گرفتمش و هیچ کلاس اموزشی هم نرفتم آشنایی با تابع Shell Function Shell(PathName, [WindowStyle]) As Double تابع shell مثل run ویندوز برای برنامه نویسی است PathName : آدرس محل برنامه ای که قرار است اجرا شود WindowStyle : حالت باز شدن پنجره برنامه می باشد کاربرد: مثلا برنامه P.exe را در شاخه "c:\Mahdi\spy" دارید. فراخوانی برنامه به صورت زیر می باشد . Call shell "c:\Mahdi\spy\p.exe" در صورتی که حالات نمایش پنجره را مشخص نکنید . مثل مثال بالا . نمایش پنجره همان نمایش پیش فرض ویندوز می باشد. استفاده از تایع sendkeys خوب اینجا می خوام یک کد کاربردی دیگه رو بهتون بگم . این کد باعث می شه که وقتی شما رویداد خاصی رو اجرا مکنید , کلید خاصی از کیبرد اجرا شود یعنی مثلاً اگر شما روی یک Textbox هستید و کلید Enter را فشردید عملی معادل فشردن کلید ..... , Tab ,Delete,Pagedown , F1 ,F2 روی دهد : Private Sub TextBox_KeyPress(KeyAscii As Integer) If KeyAscii = 13 Then SendKeys "{tab}" End If End Sub |
||
|
|
|
|
|
بخش اول : تعاریف مربوط به نمایش درختی گره ( Node ) : بهترین ایده این است که یک گره را مانند یک شاخه منشعب در یک درخت فرض کنیم. هرگره نشان دهنده یک عضو است. یک گره میتواند هر تعدتد زیر گره را نیز داشته باشد. برادر ( Subline ) : یک برادر گره دیگری است که در همان انشعاب گره مذکور قرار دارد فرزندان ( Childern ) : فرزندان به زیر گره های گره جاری گفته میشود. بعضی مواقع به انها فرزند هم گفته میشود. پدر ( Parent ) : پدر گره ای است که در بالای گره جاری قرار دارد. همه گره ها به جزء گره ریشه دارای پدر می باشند. بخش دوم : اضافه کردن گره به درخت در این بخش شما به کنترل های زیر نیاز خواهید داشت: یک کنترل Tree View که در قسمت کامپوننت و بخش Microsoft Common control 6.0 است و ان را به فرمتان بیافزایید یک کنترل ImageList که سه تصویر یا 16*16 یا 32*32 باشد که تصاویر را به صورت زیر نام گذاری کنید ////////////////////// تصویر فولدر بسته با Key >>> Closed تصویر یک فولدر باز شده با Key >> Openتصویر یک فایل با key >> Leaf//////////////////////// کنترل Tree View تعدادی گره را در خود نگه می دارد بنابراین برای اضافه کردن گره به درخت باید یک گره جدید را ابتدا تعریف کنید و سپس ان گره را به مجموعه Tree View اضافه کنید مانند مثال زیر :Dim nodx As Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node")ان را اجرا کنید. شما باید فقط یک عضو را در درخت بنام " Root Node " ببینید. دقت کنید که خاصیت Parent در مثال فوق خالی بود. این مساله برای این است که میخواستیم گره مذکور بالاترین گره یا ریشه باشدو بنابراین نباید هیچ پدری داشته باشد. اجازه بدید تعدادی گره به درختمان اضافه کنیم اما این دفعه تصاویری را نیز به گره هایمان اضافه خواهیم کرد:Dim nodx As Node Set TreeView1.ImageList = ImageList1' Add root Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node","Closed")' Expand root node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True' Create a child node under the root node Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child1", "Child node1", " Closed")' Expand this node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True' Create several more children Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child2", _" Child node 2", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child3", _" Child node 3", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child4", _" Child node 4", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child5", _" Child node 5", "Leaf")' Create two child nodes under the first child node of root Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1A", _" Child node 1 A", "Leaf") Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1B", _" Child node 1 B", "Leaf")حالا اگر به کد دقت کنید می بینید ما یک ارجاع ئاریم که گره را در خودش ذخیره میکند و Nodx نام دارد. شما میتوتنید این ارجاع را برای اصلاح خواص ان گره استفاده کنید. مانند خطوط که به شکل زیر امده اند: nodex.expanded=Trueکاری که این خط انجام میدهد باز کردن یک گره است بنابراین ما میتوانیم گره های فرزند ان را مشاهده کنیم. ان مانند وقتی است که کاربر روی یک گره کلیک می کند تا خودشان را باز کنند. این کار بوسیله این کد قایل انجام است.ادامه دهید و ان را اجرا کنید. شما خواهید دید که گره ریشه اصلی حالا 5 فرزند در زیر دارد و اولین انها دو فرزند در زیر دارد.بخش سوم : پیمایش درختی در اینجا نمونه های برای پیمایش درخت برای پیدا کردن همه گرههای زیر یک گره خاص را اوردهایم. مثال زیر مثال ساده ای است که به شما نشان میدهد چگونه همه گره های زیر یک گره خاص را بدست اورید و متن برچسب انها را نمایش دهید: Dim nodx As Node Set TreeView1.ImageList = ImageList1' Add root Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node", "Closed")' Expand root node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True' Create a child node under the root node Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child1", _" Child node 1", "Closed")' Expand this node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True' Create several more children Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child2", _" Child node 2", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child3", _" Child node 3", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child4", _" Child node 4", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child5", _" Child node 5", "Leaf")' Create two child nodes under the first child node of root Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1A", _" Child node 1 A", "Leaf") Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1B", _" Child node 1 B", "Leaf")' Loop though each child of the root node Dim i As Long' Set nodx to the first child node of root. Set nodx = TreeView1.Nodes("Root").Child' Loop though each child nod assigning it to nodx For i = 1 To TreeView1.Nodes("Root").Children MsgBox nodx.Text Set nodx = nodx.Next Nextفقط بلوک اخر کد در این مثال جدید است. شما میتوانید خاصیت Childern را برای فهمیدن تعداد گره های فرزند یک گره خاص بکار ببرید. خاصیت Children به اولین گره فرزند اشاره میکند و خاصیت Next به گره بعدی نسبت به گره جاری اشاره میکندبخش چهارم : رویدادها الان باید درباره چیزهای جدیدی صحبت کنیم. برای مثال ما میخواهیم فقط دو گره را که مانند دایرکتورب در سیستم شما هستند به ان اضافه کنیم. وقتی شما روی گره کلیک کنید ان وقت مسیر واقعی فایل برای ان فولدر را به شما میدهد: Private Sub Form_Load () Dim nodx As Node Set TreeView1.ImageList = ImageList1' Add Drive Set nodx = TreeView1.Nodes.Add(, , , "c:", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True' Add Folder Set nodx = TreeView1.Nodes.Add(nodx, tvwChild, , "Windows", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True' Add Another Folder Set nodx = TreeView1.Nodes.Add(nodx, tvwChild, , "System", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True End Sub Private Sub TreeView1_NodeClick(ByVal Node As MSComctlLib.Node) MsgBox Node.FullPath End Subوقتی ما روی یک گره در درخت کلیک میکنیم رویداد Node Click روی میدهد و ان گره ای را که کلیک شده بود را به ما بر میگرداند بنابراین دستکاری ان بسیار اسان است. به نظر شما یز خطرناکی در مثال فوق وجود دارد؟ این صحیح است در اینجا هیچ کلیدی برای هر کدام از گره ها وجود ندارد. کلید اختیاری است. دقت کنید که چون من کلیدی ندارم به جای فقط کلید گره که در تابع add برای اضافه کردن یک گره به درخت استفاده میشود. خود گره را بدست میاورم. این علت ارسال شی Nodx به تابع add می باشد. چون ان واقعا فقط یک اشاره گر به گره قبلی است. شما باید قادر باشید که ذر مثال فوق ببینید که کنترل Tree View بخوبی برای نمایش سیستم فایل کارذمیکند. مانند اکسپلورر در ویندوز خاصیت FiullPath هر چیزی را که ما درباره محل فایل نیاز داریم را به خواهد داد. در اینجا یک مثال از رویدادهای Node Click و collapse و Expand است: Private Sub Form_Load() Dim nodx As Node Dim nodr As Node' Show Root Lines TreeView1.LineStyle = tvwRootLines' Display Checkboxes TreeView1.Checkboxes = True' Add Items Set nodx = TreeView1.Nodes.Add(, , , "Item 1") Set nodx = TreeView1.Nodes.Add(, , , "Item 2") Set nodx = TreeView1.Nodes.Add(, , , "Item 3") Set nodx = TreeView1.Nodes.Add(, , , "Item 4") Set nodx = TreeView1.Nodes.Add(, , , "Item 5") nodx.Expanded = True Set nodr = TreeView1.Nodes.Add(nodx, tvwChild, , "Item 6") nodr.Expanded = True Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 7") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 8") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 9") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 10") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 11") Set nodx = TreeView1.Nodes.Add(, , , "Item 12") End Sub Private Sub TreeView1_Collapse(ByVal Node As MSComctlLib.Node) MsgBox "Colapsing node: " & Node.Text End Sub Private Sub TreeView1_Expand(ByVal Node As MSComctlLib.Node) MsgBox "Expanding node: " & Node.Text End Sub Private Sub TreeView1_NodeCheck(ByVal Node As MSComctlLib.Node) If Node.Checked Then MsgBox "Node " & Node.Text & " was checked" Else MsgBox "Node " & Node.Text & " was Unchecked" End If End Subان را اجرا کنید و روی چک باکسها کلیک کنید. شما باید پیغامی را در یافت کنید که به شما میگوید که این گره شما فقط چک خورده است. حالا سعی کنید بعضی از گره ها را جمع کنید و یا باز کنید و در این حالتها شما پیغامی را از رویدادهای Collapse و Expand در یافت خواهید کرد |
||
|
|
|
|
|
تاریخچه زبان ویژال بیسیک با شناختی که از تاریخچه زبان ویژوال بیسیک بدست می آورید راحت تر می توانید از آن استفاده کنید. شرکت مایکروسافت ویژوال بیسیک را براساس یک زبان برنامه نویسی به نام بیسیک که برای مبتدیان نوشته شده ساخت. زبان ویژوال بیسیک بیشتر از 35 سال به اشکال مختلف رایج بوده. در واقع طراحان این زبان می خواستند یک زبان برنامه نویسی برای استفاده مبتدیان طراحی کنند.برنامه نویسان جدید می توانند با استفاده ازبیسیک به سرعت به شرع برنامه نویسی های حرفه ای با زبان های cobol .fortran . assembler در مقایسه به بیسیک کار بیشتری نیاز داشت. طبیعت بصری ویژوال بیسیک دیدید که ویژال بیسیک چیزی بیشتر از یک زبان برنامه نویسی است. از ویژوال بیسیک در نام آن visual به معنای بصری یا محیط نمایشی است. کار با ویژال بیسیک در اولین بار که برنامه را باز می کنید با پنجره new project روبه رو می شوید در این قسمت نوع فرم خود را انتخاب کرده ماننده activex|standard و.... این پنجره شامل 3 قسمت بوده New :در این پنجره امکان انتخاب فورم مورد نظر شما امکان پذیر می باشد.Existing :در این پنجره امکان انتخاب project های مختلف که در مکانهای مختلف ذخیره یا... امکان انتخاب می باشد.Recent :در این قسمت هر projectرا که ذخیره می کنید به صورت دسته ای جمع می شود حالا یک فایلیدر درایو Dباشد حالا چه در درایو c.DON.T SHOW THIS DIALOG IN THE FUTURE این قسمت جلو گیری از باز شدن پینجره NEW PRIJECT می باشد. HELP :از این قسمت وقتی امکان استفاده می باشد که نرم افزار MSDN را نصب کرده باشید.معرفی قسمت های بیسیک. نوار ابزار: TOOLBAR:نوار ابزار VB زیر منو قرار دارد. ویژال بیسیک کلا چهار نوار ابزار دارد:STANDARD :این نوار ابزار زیر منو ظارهر است و پیش فرض است.DEBUG :وقتی از ابزارهای رفع اشکال برای ردیابی و اصلاح اشکالات استفاده می کنید. این نوار ابزار ظاهر می شود.EDIT :این نوار ابزار برای تنظیم کردن اشیاء بر روی فرم می باشدFORM EDITOR :این نوار ابزار برای تنظیم کردن اشیاء بر روی فرم می باشد.جعبه ابزار: TOOLBOX:در این پنجره تمامی شی های مختلف برای کار بر روی فرم هستند و حتی امکان اضافه کردن به این پنجره ها می باشد. پنجره PROJECT:در این پنجره فرم های انتخابی شما با هر گروه و هر فرم مشخص شده است.پنجره PROPERTISE: این پنجره امکان تنضیمات لازم برای هر شیئی را مشخص می کنید.توابع ریاضی در ویژال بیسیک برای نوشتن برنامه های مهندسی ، محاسباتی ، گرافيکی و آماری نياز داريد تا از برخی توابع رياضی استفاده نمائيد . ويژوال بيسيک ۶ دارای مجموعه ای از توابع است که برای انجام محاسبات عددی پيش بينی شده اند . در اين مقاله ابتدا با اين توابع آشنا شده و سپس چگونگی ايجاد ساير توابع رياضی را که در ميان اين مجموعه وجود ندارند خواهيد ديد . در پايان نيز با توابع رياضی موجود در دات نت آشنا می شويد . توابع رياضی موجود در ويژوال بيسيک 1 تابع Abs (قدرمطلق) : مقدار بدون علامت يک عدد را برمی گرداند .2 تابع Atn (آرک تانژانت) : خروجی تابع عددی از نوع double است که برابر زاويه ای است که تانژانت آن عدد ورودی تابع است .3 تابع Cos ( کسينوس ) : خروجی تابع عددی از نوع double است که برابر کسينوس زاويه ورودی است .4 تابع Exp (توان نمانی) : خروجی تابع عددی از نوع double است که برابر e به توان ورودی تابع است .5 تابع Int (تابع کف يا تابع جزء صحيح) : نزديکترين عدد صحيح مساوی يا کوچکتر نسبت به عدد ورودی را برمی گرداند .6 تابع Log (لگاريتم ) : خروجی تابع عددی از نوع double است که برابر لگاريم طبيعی عدد ورودی است ( لگاريتم بر مبنای عددe يا همان Ln )7 تابع Round ( گرد کردن ) : خروجی تابع عددی از نوع double است که برابر نزديکترين عدد صحيح به مقدار عدد ورودی است .8 تابع Sgn (علامت) : خروجی تابع عددی از نوع صحيح است که نشان دهنده علامت عدد ورودی است .9 تابع Sin (سينوس ) : خروجی تابع عددی از نوع double است که برابر سينوس زاويه ورودی است .10 تابع Sqr (جذر) : خروجی تابع عددی از نوع double است که برابر ريشه دوم يا جذر عدد ورودی است .11 تابع Tan (تانژانت) : خروجی تابع عددی از نوع double است که برابر با تانژانت زاويه ورودی ( برحسب راديان ) می باشد .نکته : برای محاسبه توان n ام يک عدد ( n می توان صحيح يا اعشاری باشد ) از اپراتور ^ استفاده نمائيد . برای مثال :2 ^ 5 = 329 ^ 0.5 = 34.2 ^ 3.7 = 202.31چگونگی ايجاد ساير توابع رياضی که در ويژوال بيسيک 6 وجود ندارندجدول زير چگونگی محاسبه ساير توابع رياضی که در ويژوال بيسيک وجود ندارند را نشان می دهد سکانت Sec(X) = 1 / Cos(X )کسکانت Cosec(X) = 1 / Sin(X )کتانژانت Cotan(X) = 1 / Tan(X )آرک سينوس Arcsin(X) = Atn(X / Sqr(1-X * X ))آرک کسينوس Arccos(X) = Atn(-X / Sqr(1-X * X)) + 2 * Atn(1 )آرک سکانت Arcsec(X) = Atn(X / Sqr(X * X - 1)) + Sgn((X) -1) * (2 * Atn(1 ))آرک کسکانت Arccosec(X) = Atn(X / Sqr(X * X - 1)) + (Sgn(X) - 1) * (2 * Atn(1 ))آرک کتانژانت Arccotan(X) = Atn(X) + 2 * Atn(1 )سيونس هيپربوليک HSin(X) = (Exp(X) - Exp(-X)) / 2 کسينوس هيپربوليک HCos(X) = (Exp(X) + Exp(-X)) / 2 تانژانت هيپربوليک HTan(X) = (Exp(X) - Exp(-X)) / (Exp(X) + Exp(-X ))سکانت هيپربوليک HSec(X) = 2 / (Exp(X) + Exp(-X ))کسکانت هيپربوليک HCosec(X) = 2 / (Exp(X) - Exp(-X ))کتانژانت هيپربوليک HCotan(X) = (Exp(X) + Exp(-X)) / (Exp(X) - Exp(-X ))آرک سينوس هيپربوليک HArcsin(X) = Log(X + Sqr(X * X + 1 ))آرک کسينوس هيپربوليک HArccos(X) = Log(X + Sqr(X * X - 1 ))آرک تانژانت هيپربوليک HArctan(X) = Log((1 + X) / (1 - X)) / 2 آرک سکانت هيپربوليک HArcsec(X) = Log((Sqr(1-X * X) + 1) / X )آرک کسکانت هيپربوليک HArccosec(X) = Log((Sgn(X) * Sqr(X * X + 1) +1) / X )آرک کتانژانت هيپربوليک HArccotan(X) = Log((X + 1) / (X - 1)) / 2 لگاريتم بر مبنای NLogN(X) = Log(X) / Log(N )اعداد π و e در ويژوال بيسيکبرای استفاده از عدد پی و عدد e در برنامه های خود ثوابت زير را تعريف نمائيد :Const Pi = 3.14159265358979 Const e = 2.71828182845904 همچنين عدد پی را می توان به صورت زير تعريف کرد : Pi = 4*Atn(1 )تبديل راديان / درجه چون اکثر توابع مثلثاتی بر حسب راديان کار می کنند گاهی اوقات نياز داريم تا زاويا را از در جه به راديان و بالعکس تبديل کنيم . برای تبديل يک زاويه که بر حسب راديان می باشد به درجه آنرا در 180 ضرب کرده و سپس بر عدد پی تقسيم می کنيم : Degree(x) =x*180/Pi برای تبديل يک زاويه که بر حسب درجه بيان شده به راديان آنرا در عدد پی ضرب کرده و سپس بر 180 تقسيم می کنيم : Rad(x) =x*Pi/180 توابع رياضی و VB.Netمجموعه توابع رياضی در در ويژوال بيسيک دات نت وجود دارند بسيار قويتر و کاملتر هستند . اين مجموعه توابع در کلاس System.Math موجود می باشند :** در کلاس Math دو ثابت به اسم E و PI برای نشان دادن پايه لگاريتم طبيعی و عدد پی وجود دارند .** توابع مثلثاتی : Acos ( آرک کسينوس ) ، Asin ( آرک سينوس) ، Atan ( آرک تانژانت) ، Atan2 ( آرک تانژانت خارج قسمت تقسيم ورودی ها ) ، Cos ( کسينوس ) ، Sin ( سينوس ) ، Tan ( تانژانت )** توابع عمومی : Abs ( قدرمطلق ) ، BigMul ( حاصلضرب کامل دو عدد 32 بيتی ) ، Ceiling ( تابع سقف ) ، DivRem ( خارج قسمت نقسيم دو عدد ) ، Floor ( تابع کف ) ، IEEERemainder ( باقيمانده نقسيم دو عدد ) ، Max ( ماکزيمم بين دو عدد ) ، Min ( مينيمم بين دو عدد ) ، Round ( تابع گرد کردن ) ، Sign ( تابع علامت ) ، Sqrt ( تابع جذر )** توابع هيپربوليک : Cosh ( کسينوس هيپربوليک ) ، Sinh ( سينوس هيپربوليک ) ، Tanh ( تانژانت هيپربوليک )** توابع نمايي و لگاريتمی : Exp ( عدد e به توان مقدار ورودی ) ، Log ( لگاريتم ) ، Log10 ( لگاريتم بر پايه 10 ) ، Pow ( تابع توان )آشنايي با تابع BitBltهدف از اين مبحث آموزشي ، آشنايي با تابع BitBlt و برخي ديگر از توابع کتابخانه Win32 GDI براي انجام برخي عمليات گرافيکي مثل double buffering و خواندن sprite از فايل است .نکته : sprite به کاراکترهاي متحرکي گفته مي شود که در بازيها وجود دارد .اولين چيزي که به آن نياز داريد ايجاد يک فرم است . خاصيت ScaleMode آنرا برابر 3-Pixel قرار دهيد . پيشنهاد مي کنم که هميشه در هنگام استفاده از فرم بهمراه API از pixel براي scalemode استفاده کنيد .سپس سايز فرم را به اندازه اي افزايش دهيد تا ScaleWidth برابر 320 و ScaleHeight برابر 256 شود . توجه کنيد که خاصيت HasDC فرم را True قرار دهيد . همچنين از خاصيت AutoRedraw براي فرم استفاده نمي کنيم زيرا مي خواهيم از Double Buffering استفاده کنيم که بسيار سريعتر و کارامدتر مي باشد .مرحله بعدي declare کردن API هايي است که به آنها نياز داريم :' blittingPrivate Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long ' code timerPrivate Declare Function GetTickCount Lib "kernel32" () As Long ' creating buffers / loading spritesPrivate Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long ' loading spritesPrivate Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long ' cleanupPrivate Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long سوال : DC چيست ؟ DC و يا بعبارت ديگر Device Context ، hDC يک عدد است که به يک آدرس در حافظه اشاره مي کند که داده اي در آن ذخيره شده است . در هنگام استفاده از BitBlt براي اشاره کردن به آدرسي که داده گرافيکي در آنجا ذخيره شده ، استفاده مي شود .در مرحله بعدي نياز به ذخيره آدرسهاي DC داريم که مي سازيم . آدرسهاي DC مقادير Long هستند همچنين آنها را بصورت Public تعريف مي کنيم :' our Buffer's DCPublic myBackBuffer As Long Public myBufferBMP As Long ' The DC of our sprite/graphicPublic mySprite As Long ' coordinates of our sprite/graphic on the screenPublic SpriteX As Long Public SpriteY As Long حال بايد تابعي بسازيم که تصاوير گرافيکي درون حافظه load کند . نکته مهمي که بايد به آن توجه کنيد اينست که يک device context خودش به تنهايي هيچ داده گرافيکي ندارد و بايستي يک bitmap موجود باشد تا درون آن load شود براي مثال يک فايل bmp يا يک bitmap خالي که از آن بعنوان back buffer استفاده مي کنيد .تابعي که خواهيم نوشت يک device context منطبق با صفحه مي سازد سپس فايلهاي گرافيکي مورد نظر را درون device context قرار مي دهدPublic Function LoadGraphicDC(sFileName As String) As Long ' temp variable to hold our DC addressDim LoadGraphicDCTEMP As Long ' create the DC address compatible with' the DC of the screenLoadGraphicDCTEMP = CreateCompatibleDC(GetDC(0 ))' load the graphic file into the DC...SelectObject LoadGraphicDCTEMP, LoadPicture(sFileName )' return the address of the fileLoadGraphicDC = LoadGraphicDCTEMP End Function سوال : double-buffering چيست ؟ زمانيکه يک محيط گرافيکي مي سازيد تا درون آن چيزي را ترسيم کنيد ، شما sprite ها / گرافيکها / متن را درون حافظه blit مي کنيد ( offscrean ) سپس نتيجه نهايي را روي صفحه blit مي کنيد . اين عمل از لرزش تصوير يا flickering جلوگيري مي کند ( زماني رخ مي دهد که چندين sprite مستقيماً روي صفحه blit شوند ) و بسيار سريعتر از AutoRedraw است .قبل از اينکه مثالي براي اين تابع ذکر کنم تابع BitBlt را توضيح خواهم داد :BitBlt تابعي از کتابخانه dll “gdi32” است . اين تابع يک انتقال bit-block از داده هاي مرتبط به يک مستطيل از پيکسلها به يک device context مقصد انجام مي دهد . بعبارت ديگر داده هاي گرافيکي را از محيط گرافيکي ( يک bitmap ) به محيط گرافيکي ديگري ( screen يا يک form ) کپي مي کند . فرم کلي اين تابع بصورت زير است :Declare Function BitBlt Lib "gdi32" Alias "BitBlt " _( ByVal hDestDC As Long, _ByVal x As Long , _ByVal y As Long , _ByVal nWidth As Long , _ByVal nHeight As Long , _ByVal hSrcDC As Long , _ByVal xSrc As Long , _ByVal ySrc As Long , _ByVal dwRop As Long) As Long اولين خط بيان مي کند که ما بوسيله gdi32 DLL به تابع BitBlt دسترسي خواهيم داشت . خطوط ديگر پارامترهايي هستند که اين تابع مي گيرد :hDestDC : hDC مربوط به محيط مقصد ( اگر مي خواهيد مقصد يک فرم باشد از form.hDC استفاده کنيد و يا اينکه آدرس يک backbuffer را که ساخته ايد بدهيد )x : مختصات افقي محلي که مي خواهيد گرافيک شما ظاهر شود .y : مختصات عمدي محلي که مي خواهيد گرافيک شما ظاهر شود .nWidth : عرض گرافيک شماnHeight : ارتفاع گرافيک شماhSrcDC : hDC مربوط به محيط مبداxSrc : افست x . 0 زماني استفاده مي شود که بخواهيد از سمت چپترين گوشه گرافيک مبدا عمل blit را انجام دهيد .ySrc : افست ydwRop : مد draw اي که در زمان blitting گرافيکتان مي خواهيد استفاده کنيد ( Raster Operations يا ROP ) . اين پارامتر مقادير زير را مي تواند بگيرد :- vbSrcCopy : داده تصوير مبدا را مستقيماً در مقصد کپي مي کند .- vbSrcPaint : داده هاي تصاوير مبدا و مقصد را با هم OR مي کند ( pseudo-alphablending effect ) - vbSrcAnd : داده هاي تصاوير مبدا و مقصد را با هم AND مي کند ( pseudo-gamma effect ) - vbSrcInvert : داده هاي تصاوير مبدا و مقصد را با هم XOR مي کند - vbSrcErase : ابتدا داده تصوير مقصد را invert مي کند سپس آنرا با داده تصوير مبدا AND مي کند . - vbDstInvert : داده تصوير مقصد را invert مي کند و داده تصوير مبدا را در نظر نمي گيرد . - vbNotSrcCopy : داده تصوير مبدا را invert مي کند و آنرا مستقيماً در مقصد کپي مي کند . - vbNotSrcErase : داده تصاوير مبدا و مقصد را OR کرده و نتيجه را invert مي کند . مثالي از کاربرد BitBlt : BitBlt Form1.hDC, PlayerX, PlayerY, 48, 48, picPlayer.hDC, 0, 0, vbSrcCopy حال مي خواهيم از BitBlt در يک حلقه استفاده کنيم تا يک image را در فرم حرکت دهيم :1 – يک فايل bmp با ابعاد 32x32 بسازيد و با نام sprite1.bmp در دايرکتوري پروژه ذخيره کنيد .2 – يک دکمه در فرم قرار دهيد و نام آنرا cmdTest بگذاريد .3 – دکمه را در گوشه بالايي فرم و در سمت راست قرار دهيد . 4 – کد زير را براي event مربوط به کليک شدن دکمه بنويسيد :' Timer variables...Dim T1 As Long, T2 As Long ساخت DC براي backbuffer’myBackBuffer = CreateCompatibleDC(GetDC(0 ))ساخت يک سطح bitmap براي DC’myBufferBMP = CreateCompatibleBitmap(GetDC(0), 320, 256 )load کردن سطح bitmap خالي درون buffer’SelectObject myBackBuffer, myBufferBMP قبل از blit کردن درون بافر بايد آنرا با black پر کنيم’BitBlt myBackBuffer, 0, 0, 320, 256, 0, 0, 0, vbWhiteness load کردن split توسط تابعي که در بالا نوشتيم’mySprite = LoadGraphicDC(App.Path & "\sprite1.bmp ")cmdTest.Enabled = False == شروع حلقه اصلي ==’ خواندن tickcount جاري’T2 = GetTickCount Do DoEvents T1 = GetTickCount اگر 15 ميلي ثانيه گذشته بود فريم بعدي شروع شودIf (T1 - T2) >= 15 Then پاک کردن محل قبلي sprite بوسيله پر کردن آنجا با blackBitBlt myBackBuffer, SpriteX - 1, SpriteY - 1,32, 32, 0, 0, 0, vbBlackness Blit کردن sprite درون back buffer’BitBlt myBackBuffer, SpriteX, SpriteY, 32, 32,mySprite, 0, 0, vbSrcPaint Blit کردن backbuffer روي فرم’BitBlt Me.hdc, 0, 0, 320, 256, myBackBuffer,0, 0, vbSrcCopy حرکت دادن sprite روي صفحه’SpriteX = SpriteX + 1 SpriteY = SpriteY + 1 ' update timerT2 = GetTickCount End If Loop Until SpriteX = 320 سپس بايد يک cleanup code بنويسيد تا حافظه هاي را که براي نگهداري تصاوير گرافيکي و buffer ها استفاده کرده ايد آزاد کنيد :Private Sub Form_Unload(Cancel As Integer )DeleteObject myBufferBMP DeleteDC myBackBuffer DeleteDC mySprite End End Sub اگه نظرات بیشتر از ۱۵ نشه دیگه ناراحت میشم ها |
||
|
|
|
|
|
قبل از اینکه اموزش ListView رو بدم شما به این اشیا نیاز داریدتوضیح : ListView در قسمت component وی بی و در بخش Microsoft common control 6.0 استیک عدد ListView که اونو فقط روی فرم قرار بدیدیک عدد imageList1 که فقط یک ایکون 16*16 درونش قرار بدیدیک عدد دیگر ImageList2 که فقط یک ایکون 32*32 درونش قرار بدیدبخش اول : اضافه کردن ایتم ها به ListViewبرای اینکه بتوانیم با ListView هر کاری را انجام دهیم ابتدا باید تعدادی ایتم به ان اضافه کنیم. این کار اسان است.همه چیزی که شما احتیاج دارید این است که یک شی از ListItem بسازید و سپس به یک خط برای ساختن ایتم احتیاج خواهیم داشت. به مثال زیر توجه کنیدDim itmx As ListItem Set itmx = ListView1.ListItems.Add(, , "Item1 ")این همه ان چیزی است که برای یک ایتم ساده به ان احتیاج داریم. کد بالا را به رویداد لود فرم بیافزاید و اجرا کنید. شما میتوانید ان ایتم را در ListView ببینید. اما تا اندازه ای ساده به نظر میرسد. در اینجا هیچ تصویری نمایش داده نشده است. برای نمایش تصاویر در ListView شما باید ان را به یک کنترل Image List (یا چند کنترل Image List) الحاق کنید. برای انجام این کار فقط روی خاصیت Custom کلیک کنید و زمانی که صفحه خواص برای ListView نمایش داده شد روی برگه Image List کلیک کنید. برای گزینه Normal Image List > Image List1 را انتخاب کنید و برای گزینه Small Image List > Image List2 را انتخاب کنیدو سپس خارج شوید. حالا شما تصاویری را خواهید داشت که با اجرای برنامه برای هرکدام از ایتم ها خواهید داشت. بزودی تصاویر بسیاری را خواهیم داشت. برای اینکه هنگام اجرا تصویر شماره 1 از Image List1 و تصویر شماره 1 از Image List2 را نمایش دهیم فقط کافی است کد زیر را بنویسیم :Dim itmx As ListItem Set itmx = ListView1.ListItems.Add(, , "Item1",1,1 )حالا برنامه رو اجرا کنید بخش دوم >> تغییر نما چندین راه برای نمایش اطلاعات در ListView وجود دارد. مثلا میتوانید به مرورگر ویندوز نگاه کنید که میتوانید نمایش اطلاعات را در ان تغییر دهید . مثلا به شکا نمایش ایکون به شکا بزرگ یا نمایش ایکون به شکل کوچک یا ... برای دیدن هر کدام از حالات مختلف ان را در رویداد لود فرم قرار داده و اجرا کنید Dim itmx As ListItem Dim colx As ColumnHeader' Add a bunch of items Set itmx = ListView1.ListItems.Add(, , "Item1", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item2", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item3", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item4", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item5", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item6", 1, 1) Set itmx = ListView1.ListItems.Add(, , "Item7", 1, 1)' Force window to be shown.' This is just so you can see what is happening or else the' messages would come up and the window would not have' appeared yet Me.Show' Set the listview to icon ListView1.View = lvwIcon MsgBox ("You are now viewing the list in Icon View")' Set the listview to small icon ListView1.View = lvwSmallIcon MsgBox ("You are now viewing the list in Small Icon View")' Set the listview to list ListView1.View = lvwList MsgBox ("You are now viewing the list in List View")' Add a column at runtime' You can do this ahead of time in the controls' property pages Set colx = ListView1.ColumnHeaders.Add(, , "Name")' Set the listview to report ListView1.View = lvwReport MsgBox ("You are now viewing the list in Report View")در اینجا شما فقط باید خواص ListView را تنظیم کنید. ببینید که شما میتوانید نمای هر کدام انها را تغییر دهیدبخش سوم >> کار کردن با ستون ها ایا بخاطر دارید نمای Report را در مرورگر ویندوز به شکل چی دیدید؟ دران حالت بصورت چند ستونی است و شامل : نام فایل و اندازه و .. است ممکن است مشکل به نظر برسد من به شما اطمینان میدهم که خیلی ساده استاین کدها را اجرا و بررسی کنید که هر کدام بعد از این کد دارای توضیحاتی هستند Dim itmx As ListItem Dim colx As ColumnHeader' Add some columns Set colx = ListView1.ColumnHeaders.Add(, , "Filename") Set colx = ListView1.ColumnHeaders.Add(, , "Type") Set colx = ListView1.ColumnHeaders.Add(, , "Size") Set colx = ListView1.ColumnHeaders.Add(, , "Date")' Add an item. The text here is always the' First Column (Index 0) Set itmx = ListView1.ListItems.Add(, , "Abstract.exe", 1, 1)' Here is how we access each of the columns itmx.SubItems(1) = "Program File" itmx.SubItems(2) = "15 KB" itmx.SubItems(3) = "10/10/1999"' Set the listview to reportListView1.View = lvwReport شما می توانید ببینید که ما فقط به اضافه کردن تعدادی از ستونها احتیاج داشتیم مانند مثال قبلی. تفاوتی که در اینجا وجود دارد این است که ما به SubItem ها در یک ListItem دسترسی داریم. وقتی شما ابتدا یک ایتم متن را اضافه کنید ان را در Column(0) دریافت خواهید کرد.سپس برای قرار دادن متن در ستونهای دیگر ما لازم است که متن را به هر کدام از زیر ایتمها انتقال دهیم.برای مثال بالا من خودم را برای اضافه کردن تصاویر به header ستونها دردسر ندادم امل اگر شما از VB6 استفاده میکنید میتوانید این کار را نیز انجام دهید.بخش چهارم >> رویدادهای ListViewدر اینجا دو تا از رویدادهای اصلی که شما هنگام کار با ListView واقعا به انها احتیاج داردید توضیح میدهم. رویدادهای itemClick و ItemCheck.رویداد ItemClick وقتی روی میدهد که روی لیست ایتمها کلیک شده باشد. این رویداد شی ListItem را که روی ان کلیک شده است را بر میگرداند و بنابراین شما میتوانید با ان هر کاری را که لازم است انجام دهید.پس این کد را به مثال قبلی اضافه کنید Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem) MsgBox Item.Text End Subوقتی شما برنامه را اجرا کنید و روی هر کدام از ایتمها در ListView کلیک کنید یک پیغام مبنی حاوی متن ایتمی که روی ان کلیک شده نمایش میدهدرویداد دیگر برای وقتی است که شما حالتی را استفده کرده باشید که کنار هر کدام از ایتمهای ListView یک جعبهچک نمایش داده شده باشد. این کد را به رویداد لود فرم اضافه کنید:ListView1.Checkboxes = True وقتی شما برنامه را اجرا کنید متوجه خواهید شد که یک جهبه چک در کنار هر کدام از ایتم ها داده شده است. ممکن است تا زمانی که روی ListView کلیک نشود ان را به روز نکند ( به نظر میرسد این یکی از باگهای VB باشد). اما اگر شما مقادیر جعبه چکها را در زمان طراحی تغییر دهید ان را بدرستی نمایش خواهد داد. برای انجام دادن کاری در زمان که کاربر جعبه چکها را تیک میزند و یا تیک را برمیدارد شما باید کد زیر را در رویداد ItemCheck قرار دهید.Private Sub ListView1_ItemCheck(ByVal Item As MSComctlLib.ListItem ) If Item.Checked Then MsgBox "Box is being checked" Else MsgBox "Box is being unchecked" End If End Subبخش پنجم >> مرتب کردن ListView بوسیله ستون ها:در این بخش ما میخواهیم ایتم های را در حالت Report به ListView اضافه کنیم و وقتی که کاربر روی عنوان یکی از ستونها کلیک کرد میخواهیم ایتم های موجود در ListView را بر اساس همان ستون مرتب کنیم. اگر کاربر روی همان ستون دوباره کلیک کرد این مرتب سازی بین حالت صعودی و نزولی تغییر کند. ابتدا نگاهی به کد زیر میاندازیمکه تا اندازه ای واضح است:Private Sub Form_Load () Dim itmx As ListItem Dim colx As ColumnHeader' Add Some Columns Set colx = ListView1.ColumnHeaders.Add(, , "Col1") Set colx = ListView1.ColumnHeaders.Add(, , "Col2")' Add two items Set itmx = ListView1.ListItems.Add(, , "ABC") itmx.SubItems(1) = "XYZ" Set itmx = ListView1.ListItems.Add(, , "XYZ") itmx.SubItems(1) = "ABC"' Set the view to report ListView1.View = lvwReport End Sub Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)' Check if the Sortkey is the same a the current one If ListView1.SortKey <> ColumnHeader.Index - 1 Then' When a column is clicked set the sortkey' to the columnheader index -1 ListView1.SortKey = ColumnHeader.Index - 1 ListView1.SortOrder = lvwAscending Else' If the column is already selected then change the' sortorder to be the opposite of what is currently' being used ListView1.SortOrder = IIf(ListView1.SortOrder = lvwAscending, _ lvwDescending, lvwAscending) End If' Set the sorted property to use the new sortkey' and sort the contents ListView1.Sorted = True End Subبنابراین وقتی کاربر روی یک ایتم کلیک میکند رویداد Column_Click رخ میدهد که مرجعی به شی ColumnHeader که کلیک شده است برمیگرداند. در این کد ما این را برای دسترسی به خاصیت index برای مقدار دهی SortKey از کنترل ListView بکار برده ایم ( SortKey فقط میگوید ListView چگونه مرتب شود ) وقتی که این کامل شد ما مقدار خاصیت Sorted کنترل ListView را به True مقدار میدهی.این اطمینان حاصل میکند که کنترل مرتب شده است و بوسیله این تغییرات در SortKey منعکس شده و نمایش داده شود. اگر همان ستون دوباره کلیک شد سپس ما نوع مرتب سازی را تغییر میدهیم و ان را برابر عکس روش فعلی قرار میدهیمبخش ششم >> ذخیره کردن عناوین ستونهای مرتب شده : ایا تاکنون شما برنامه های را دیده اید که چیزهایی را در ان مطابق میلتان تنظیم کرده اید و سپس از برنامه خارج شده و وقتی که دوباره به برنامه برگشته اید همه تنظیمات ان به حالت پیش فرض برگشته است و هیچ کدام از تنظیمات مورد نیاز شما را ذخیره نکرده باشد؟ این مساله بی نهایت ازار دهنده است و نشان دهنده یک برنامه نویسی ضعیف است. برای کنترل ListView این مساله در رابطه با ColumnHeader صدق میکند. اگر شما اجازه دهید که کاربر اندازه یا نوع مرتب سازی ستونها را تغییر دهد باید این تنظیمات را ذخیره کنید بنابراین دفعه بعد که کاربر از برنامه شما استفاده کند این تنظیمات را در اختیار خواهد داشت. در اینجا کدی را که شما برای انجام دادن این کار نیاز دارید قرار دادم البته بیشتر کدها رو قبلا گفتم و فقط یه مقدارش جدید استPrivate Sub Form_Load () Dim colx As ColumnHeader' Let the user reorder the columns ListView1.AllowColumnReorder = True' Set view to report ListView1.View = lvwReport' Add some columns Set colx = ListView1.ColumnHeaders.Add(, , "Col1") Set colx = ListView1.ColumnHeaders.Add(, , "Col2") Set colx = ListView1.ColumnHeaders.Add(, , "Col3") Set colx = ListView1.ColumnHeaders.Add(, , "Col4") Set colx = ListView1.ColumnHeaders.Add(, , "Col5") Set colx = ListView1.ColumnHeaders.Add(, , "Col6")' Loop though each ColumnHeader object and set the' position of it dependent on what the user did' the last time For Each colx In ListView1.ColumnHeaders colx.Position = GetSetting(App.Title, "Settings", "Col" & colx.Index,colx.Index) colx.Width = GetSetting(App.Title, "Settings", "ColWidth" & colx.Index, colx.Width) Next End Sub Private Sub Form_Unload(Cancel As Integer) Dim colx As ColumnHeader' Save the Position of each of the ColumnHeader' Objects so we can load them the next time' the user starts the program For Each colx In ListView1.ColumnHeadersSaveSetting App.Title, "Settings", "Col" & colx.Index,ListView1.ColumnHeaders(colx.Index).Position SaveSetting App.Title, "Settings", "ColWidth" & colx.Index,ListView1.ColumnHeaders(colx.Index).Width Next End Subرویداد Unload فرم تا زمانی که همه ColumnHeader ها در کنترل ListView را بدست اورده و مکان و اندازه (عرض) انها را ذخیره کند ادامه پیدا میکند.سپس دفعه بعد که برنامه اجرا شود در رویداد لود فرم این خواص اشاره شده و مقادیر انها را به اشیای ColumnHeader خاص برمی گردانیم.چند روز دیگه اموزش تری ویو رو میزارم سر بزنید |
||
|
|
|
|
|
اموزش بعدی که عکسشم گذاشتم شبیه سازی Power DVD 6 است حتما سر بزنیدبچه ها از نظراتتون متشکرم. اقا بهنام و امیر و بهروز و مسعود دارم روی نظراتتون کار میکنم و در اولین فرصت پاسخ میدم
|
||
|
|
|
|
|
فرستادن پیام کوتاه با ویژوال بیسیک توجه : یه کتاب گرفتم : 211 نکته برای برنامه نویسان ویژوال بیسیکابتدا یک پروژه استاندارد باز کنید و سپس یک ماژول هم به اون اضافه کنید و 4 تا TextBox و 4 تا Label و 2 تا Command به فرم بیافزاییدو بعدش نام TextBox ها رو مثل شکل قرار بدید و نام command1 رو btnSend بزاریدlabel ها رو هم دلخواه قرار بدید حالا این کدها رو هم تو فرمتون کپی کنید
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Sub btnSend_Click ()On Error Resume Next btnSend.Enabled = False MsgBox (SendMessage(txtusername.Text, txtpassword.Text, txtNumber.Text, txtMessage.Text ))btnSend.Enabled = True End Sub Private Sub Form_Load ()txtusername.Text = "User@host.com "End Sub حالا این کدها رو هم تو ماژولتون کپی کنید
Public Function SendMessage(username As String, password As String , _ destination As String, message As String)Dim xmlstring As String xmlstring = ") & "?> " & _ "< Request xmlns:xsi=" & Chr(34) & "http://www.w3.org/2001/XMLSchema-instance" & Chr(34) & " xsi:noNamespaceSchemaLocation=" & Chr(34) & "http://schema.2sms.com/1.0/0410_RequestSendMessage.xsd" & Chr(34) & " Version = " & Chr(34) & "1.0" & Chr(34) & ">" & _"< Identification>" & _"< UserID>>" & _"< Password>" & password & ">" & _"< FONT>Identification>" & _ "< Service>" & _"< ServiceName>SendMessage>" & _"< ServiceDetail>" & _"< SingleMessage>" & _"< Destination>" & destination & ">" & _"< Text>>" & _"< FONT>SingleMessage>" & _ "< FONT>ServiceDetail>" & _ "< FONT>Service>" & _ "< FONT>Request>" ' open connection to server and sendDim xmlrequest As MSXML2.XMLHTTP Set xmlrequest = New MSXML2.XMLHTTP xmlrequest.Open "POST", "http://www.2sms.com/xml/xml.jsp", False xmlrequest.setRequestHeader "content-type", "text/xml "xmlrequest.send xmlstring ' get the response backresponse = xmlrequest.responseText ' set up DOM to parseDim xmlresponse As MSXML2.DOMDocument30 Set xmlresponse = New MSXML2.DOMDocument xmlresponse.async = False xmlresponse.resolveExternals = False xmlresponse.validateOnParse = False On Error Resume Next xmlresponse.loadXML response ' pull out relevant variables from responsejavaresult = (xmlresponse.getElementsByTagName("Result").Item(0).Text )errorCode = (xmlresponse.getElementsByTagName("ErrorCode").Item(0).Text )errorreason = (xmlresponse.getElementsByTagName("ErrorReason").Item(0).Text )messageid = (xmlresponse.getElementsByTagName("MessageID").Item(0).Text )If errorCode = "00" Then SendMessage = javaresultElse If errorreason = "" Then SendMessage = "Message Failed - Unknown Error" Else SendMessage = "Message FAILED. (Reason: " & errorreason & ")" End IfEnd If End Function بچه ها من خودم تست نکردم ولی فکر کنم باید تو یه سایتی ثبت نام کنید حالا بریم سراغ ولتمتر برای بچه های الکترونیک
یه پروژه استاندارد درست کنید و سپس 2 تا Radio button و 1 دونه Textbox و از قسمت کامپوننت هابا زدن Ctrl + T ابزار Microsoft Comm Control 6.0 رو انتخاب و در اخر هم یک Command به فرمتون اضافه کنیدRadio 1 = optEnable Radio 2 = optstop text = txtVolts cmd = cmdexit MSComm = MSComm1 حالا این کدها رو تو فرمتون کپی کنید Option Explicit Private fEnable As Boolean Private Sub cmdExit_Click () If MSComm1.PortOpen = True Then MSComm1.PortOpen = False End If EndEnd Sub Private Sub Form_Load () MSComm1.InputLen = 0 MSComm1.CommPort = 1 MSComm1.Settings = "9600,N,8,1"End Sub Private Sub optEnable_Click () fEnable = True Do Until fEnable = False DoEvents Dim BytesToRead As Integer Dim DataIn As Variant MSComm1.PortOpen = True BytesToRead = 1 Do DoEvents Loop Until MSComm1.InBufferCount = BytesToRead DataIn = MSComm1.Input txtVolts.Text = Asc(DataIn) * 0.0196 & " Volts DC" MSComm1.PortOpen = False LoopEnd Sub Private Sub optStop_Click () fEnable = FalseEnd Sub حالا برنامه رو اجرا کنید و باید از طریق درگاه کام یه ولتاژ دلخواه رو اعمال کنید اموزش برنامه تبدیل عدد به زمان برای کارهای مولتی مدیا فوق العاده کاربردی
ابتدا یک فرم درست کنید و بعد دوتا TextBox قرار بدید و این کدها رو تو فرمتون کپی کنیدنکته : اینکه TextBox 1 برای وارد کردن عدد مورد نظر شماستPrivate Sub Text1_LostFocus ()Dim isec As Integer isec = Val(Text1.Text )Dim breaksec breaksec = Str$(Int(isec / 60)) & " ÏÞíÞå æ " & Str$(isec Mod 60) & " ËÇäíå "Text2.Text = breaksec End Sub |
||