Saturday, September 12, 2009

Μετέφερα το blog στο http://shock.grog.gr





Read more ...

Sunday, August 30, 2009

Amiga Legent Part 1


Τρία πράγµατα µου έκαναν εντύπωση από τότε που ξεκίνησα την ενασχόληση µου µε τους υπολογιστές εκεί γύρω στις αρχές της δεκαετίας του 80.
Το πρώτο ήταν ...η πρώτη µου επαφή µε υπολογιστή. Εκείνος ο µικρός, σχεδόν ψεύτικος Z80 µε τις µεµβράνες για πληκτρολόγιο. Μου έκανε τόση εντύπωση που παρόλο που ήµουν δεν ήµουν 8 ετών πήγαινα µε το λεωφορείου στα µαγαζιά το Σάββατο το πρωί και χάζευα τις οθόνες/τηλεοράσεις στα μαγαζιά της Στουρνάρη.
Το δεύτερο πράγµα που µου έκανε τροµερή εντύπωση ήταν το internet. Αυτό το έζησα µέσα από τις gateways των BBS και θα έλεγα ότι είναι µια επανάσταση όχι μόνο στο χώρο της πληροφορικής αλλά σχεδόν σε κάθε πτυχή της καθηµερινότητας µας. Το ότι διαβάζετε αυτή τη στιγµή ένα κείµενο που θα μπορούσε να ήταν ένα αρχείο μόνο στον σκληρό δίσκο του υπολογιστή µου, δείχνει ότι το internet είναι εκτός από ένα τεχνολογικό επίτευγµα και µια επανάσταση στις σχέσεις των ανθρώπων και στον τρόπο επικοινωνίας τους.
Το internet το γνώρισα μάλλον σε μεγάλη ηλικία μιας και ήµουν ήδη 20 ετών όταν έκανα βόλτες στην Αριάδνη και στις BBS. Ίσως για αυτό δε μου έκανε τόσο εντύπωση στην αρχή και κατάλαβα τα προτερήµατα του σιγά-σιγά. Μεγάλωνα εγώ, µεγάλωνε και αυτό, κάποια στιγµή απόκτησε και τη γνώριµή "δική µας" μορφή µε τη χρήση browser.
Όλα αυτά όµως δεν ήταν τίποτα σε σχέση µε το σοκ που έπαθα όταν πρωτοαντίκρισα την Amiga. Σήµερα έχουµε defacto κάποια πράγµατα. Οι δικοί µας µας πρήζουν ότι δε ζήσαµε τη φτώχεια και τη µιζέρια ενός πολέµου και έχουµε defacto το γεµάτο πιάτο, εµείς σήµερα έχουµε defacto τα εντυπωσιακά γραφικά, τα Multimedia, τα mp3, τα φοβερά παιχνίδια, τη διαχείριση του υπολογιστή µέσω παραθύρων (και όχι δεν εννοώ το λειτουργικό σύστηµα windows) και άλλα πολλά.
Οι περισσότεροι όµως δεν ξέρουν ότι οι περισσότερες ανέσεις αλλά και η τεχνολογία που χρησιµοποιείται σήµερα δεν υπήρχαν µέχρι πριν το 1985. Τότε ήταν που παρουσιάστηκε στο ευρύ κοινό ένας υπολογιστής που ήταν κατά τη γνώµη µου 10 χρόνια µπροστά από τον ανταγωνισµό.
Χµµ τώρα που το σκέφτοµαι διάλεξα λάθος λέξη. Όταν µιλάς για ανταγωνισµό µιλάς µε τη γλώσσα των managers, µιλάς για προϊόντα, για αγορά, target groups και άλλες µισητές σε µένα έννοιες.
Η Amiga όµως δεν ήταν προϊόν, δεν ήταν ένα δηµιούργηµα κάποιου marketing τµήµατος που έκανε έρευνα στην αγορά και έδωσε τα specs στους τεχνικούς για να τους φτιάξουν ένα προϊόν. Όχι τουλάχιστον µέχρι τη στιγµή που τελείωναν τα χρήµατα και έπρεπε οι σχεδιαστές της να κάνουν κάτι για να τη κρατήσουν ζωντανή.
Έχει αποδειχτεί ότι οι µεγαλύτερες επιτυχίες εκείνη την εποχή ήταν αποτέλεσµα του έρωτα κάποιων τεχνικών µε τη δουλειά τους και οι απίστευτοι υψηλοί στόχοι που βάζανε για να δηµιουργήσουν κάτι που θα τους έκανε περήφανους.

Κανείς δεν ζήτησε από τον Carl Sassanath να φτιάξει preemptive multitasking λειτουργικό σύστηµα. Εκείνος όµως δούλευε 100 ώρες την εβδοµάδα για κάτι στο οποίο πίστευε και είχε µεράκι να φτιάξει.

Η ιστορία της Amiga είναι κάτι το οποίο κατ’εµέ έχει πολύ ενδιαφέρον. Όλα ξεκίνησαν από έναν υπάλληλο της Atari τον Jay Miner. O εν λόγω κύριος γεννηθείς 31 Μαίου του 1932 ήταν συνιδρυτής της Synertek µιας εταιρίας που έφτιαχνε MOS 6502 επεξεργαστές και custom chips εκτός των άλλων. Όταν η Atari ήθελε να σχεδιάσει τη γνωστή κονσόλα 2600 αποτάνθηκε στην Synertek και συγκεκριµένα στον Miner για να τη βοηθήσει.
Αργότερα ο Miner έγινε υπάλληλος της Atari και σχεδίασε τα Atari 400 και 800. Αρχές του 80 ο Miner διάβασε για έναν νέο επεξεργαστή που έβγαζε η Motorola τον 68000. Αµέσως πλησίασε τους managers της Atari και τους πρότεινε την δηµιουργία ενός νέου υπολογιστή κάτι σαν τον διάδοχο του 800 αλλά αυτή τη φορά στα 16bits. Αυτοί πρωτότυπο για τους managers εκείνης της εποχής αρνήθηκαν γιατί ο 68000 τότε κόστιζε 100$ και πίστευαν ότι το τελικό προϊόν θα ήταν πολύ ακριβό.
∆εν καταλάβαιναν όµως ότι οι τιµές πέφτανε και ότι µέχρι τη στιγµή της κυκλοφορίας του υπολογιστή ο 68000 θα κόστιζε πολύ λιγότερο.
Ο Miner 50 ετών πια µετά από την απόρριψη της πρότασης του από την Atari έφυγε και πήγε και δούλεψε σε διάφορες άλλες εταιρίες. Ένας άλλος υπάλληλος της Atari, πρώην συνάδελφος πια του Miner, ο Larry Kaplan ξεκίνησε την Activision µαζί µε τρεις-τέσσερις άλλους συνεταίρους. Με το που την έφτιαξε όµως ήθελε να φύγει και να φτιάξει µια καινούρια εταιρία παιχνιδιών.
Ρωτάει λοιπόν τον Miner αν ξέρει κάποιον µε λεφτά για να επενδύσει στην νέα εταιρία. Ο Miner τότε δούλευε σε µια εταιρία που έφτιαχνε chips την Xymos. Tο αφεντικό του εκεί ήξερε κάποιους οδοντίατρους στην Florida που είχαν αρκετά χρήµατα και θέλανε να τα επενδύσουν κάπου. Τα ηλεκτρονικά παιχνίδια τότε ήταν κάτι πρωτόγνωρο και κάνανε αρκετή εντύπωση. Οι οδοντογιατροί δεν είχαν ιδέα βέβαια για το τι χρειάζεται µια εταιρία παιχνιδιών για να πετύχει, πόσο µεγάλη είναι η πιθανότητα αποτυχίας και πόσο δύσκολος είναι ο ανταγωνισµός. Αν το ξέρανε θα επενδύανε στη δεύτερη επιλογή τους που ήταν µια εταιρία παγωτών!
Το 1982 λοιπόν δηµιουργήται η εταιρία Hi-Toro που σκοπός της ήταν να φτιάχνει παιχνίδια για το 2600, και να σχεδιάζει chips επόµενης γενιάς τα οποία θα τα κατασκεύαζε η Xymos. Ο Miner ήταν φυσικά υπεύθυνος για τα chips αυτά µέχρι τη στιγµή που ο Larry την έκανε για άλλη µια φορά και έτσι ο Miner ανέλαβε βασικά τα πάντα. Για να γίνει αυτό όµως έπρεπε να αφήσει τη δουλειά του στην Xymos να πάρει το σκύλο του τον Mitchy και να πάει στην Santa Clara όπου ήταν τα γραφεία της νέας εταιρείας.
H αρχή έγινε και το όνειρο του Miner για έναν 16bito υπολογιστή είχε ελπίδες να γίνει πραγµατικότητα. Υπήρχε µόνο ένα πρόβληµα, έπρεπε να πάρει την άδεια των οδοντιάτρων για να µπορέσει να συνεχίσει. Αφού πήρε το πράσινο φως και από εκεί, άρχισε να στήνει την οµάδα. Πρώτο πρώτο έφερε έναν συνάδελφο του από την Atari τον Joe Demair, οι υπόλοιποι ήταν από άλλες εταιρίες που είχαν όµως σχέση µε τα παιχνίδια ή την υψηλή τεχνολογία (για παράδειγµα ο Mical έφτιαχνε arcade για την Williams) Κάπου εκεί άλλαξε και το όνοµα της εταιρίας µιας και δεν άρεσε στους οδοντίατρους. Το όνοµα Amiga γεννήθηκε από την αναζήτηση σε ένα λεξικό ενός ονόµατος που θα βρίσκεται ΠΡΙΝ τις λέξεις Apple και Atari. Έτσι οι φίλοι µας βρήκαν τη λέξη Amigo που είναι µια ισπανική λέξη που όµως υπάρχει και στο Αµερικάνικο λεξικό, Αργότερα το Amigo έγινε Amiga και απέκτησε και logo.
Ο υπολογιστής που είχε στο µυαλό του ο Miner έπρεπε να έχει τις ίδιες ικανότητες στα γραφικά µε τα workstations στους εξοµοιωτές αεροπλάνων. Ο Miner είχε µανία µε τα αεροπλάνα και όταν είδε πρώτη φορά έναν τέτοιο εξοµοιωτή άρχισε να σκέφτεται τι καλά θα ήταν να µπορούσε ο υπολογιστής του να έχει παρόµοιες δυνατότητες απεικόνισης γραφικών. Οι στόχοι λοιπόν ήταν πολύ υψηλοί και για να επιτευχθούν έπρεπε να δηµιουργηθεί τεχνολογία που µέχρι τότε ήταν αδιανόητη για home computer..
Για παράδειγµα ο υπολογιστής έπρεπε να διαθέτει υψηλές αναλύσεις µε τουλάχιστον 16 ή και 32 χρώµατα από παλέτα 4096 χρωµάτων. Θα έπρεπε να έχει κυκλώµατα που θα επιταχύνουν τη µεταφορά δεδοµένων και να έχει συνεπεξεργαστές για να ανακουφίζουν τον κεντρικό επεξεργαστή από διαδικασίες που δεν χρειάζονται λογική.
Σήµερα µπορεί να µιλάµε για 24 bits κάρτες γραφικών µε 3D accelerators αλλά όλα αυτά ξεκίνησαν από την Amiga και µάλιστα το 1983-4. Σήµερα έχουµε τον πολυκάναλο ψηφιακό ήχο µε τα samples και τα Mp3 δεδοµένα αλλά όλα αυτά δεν υπήρχαν πριν ο Miner πει ότι θέλει τέσσερα ανεξάρτητα κανάλια για samples, µε µονάδα FM synthesizer και φίλτρα στον υπολογιστή του. Σήµερα µιλάµε για vectors και τρίγωνα αλλά όταν ο Miner έβαζε δυνατότητα παραγωγής γραµµών και γέµισµα (fill) περιοχών κανείς δεν ήξερε το γιατί και το πως. Αυτά τα τεχνικά χαρακτηριστικά ήταν απλά αδιανόητα για τους τότε σπιτικούς υπολογιστές αλλά είπαµε ο Miner ήθελε τον δικό του εξοµοιωτή.
Το πιο ενδιαφέρον ήταν ότι τα διάφορα “συστατικά” του υπολογιστή δούλευαν ανεξάρτητα του 68000.
Βλέπετε οι επεξεργαστές καταλαβαίνουν εντολές. Μια εντολή είναι για παράδειγµα και η “πρόσθεσε τον αριθµό 5 στον αριθµό που υπάρχει στην τάδε διεύθυνση µνήµης”. Ο επεξεργαστής για να ολοκληρώσει αυτή την εντολή θέλει κάποιο χρόνο ο οποίος µετριέται σε κύκλους ρολογιού. Αυτό το PC που αγοράσατε µε τον Pentium στα 3.0 Ghz έχει κάποιο κύκλωµα σαν κρύσταλλο στο motherboard που παράγει σήµατα συγχρονισµού προς τον επεξεργαστή µε ρυθµό 3000000 “κλικς” το δευτερόλεπτο. Ο επεξεργαστής τώρα ανάλογα το µοντέλο και την σχεδίαση του µπορεί στο πρώτο “κλικ” του ρολογιού να “φέρει” από τη διεύθυνση που του είπαµε τον αριθµό, στο δεύτερο “κλικ” να κάνει την πρόσθεση µε τον σταθερό αριθµό που του δώσαµε και σε ένα τρίτο “κλικ” να αποθηκεύσει το αποτέλεσµα σε έναν άλλο καταχωρητή. Μια τόσο απλή εντολή πήρε τρεις κύκλους ρολογιού, υπάρχουν άλλες εντολές που παίρνουν αρκετά περισσότερο. Μια µέθοδο που ακολούθησε ο Miner για να επιταχύνει την Amiga ήταν να µην περιµένει τον 68000 να τελειώσει την κάθε εντολή για να κάνει κάτι άλλο. Τα διάφορα υποσυστήµατα γραφικών ήχου, εισόδου/ εξόδου περνάγανε µέσα από έναν DMA controller και µιλάγανε απευθείας µε την µνήµη αντί να περιµένουν τον 68000. Έτσι στο δεύτερο “κλικ” του ρολογιού που λέγαµε παραπάνω η µνήµη ήταν ελεύθερη για προσπέλαση και ο copper (ο συνεπεξεργαστής του 68000) µπορούσε για παράδειγµα να αρχίσει να γεµίζει µε τιµές τους καταχωρητές συστήµατος. Με το που τελείωνε την πρόσθεση του ο 68000 ξαναέπερνε τον έλεγχο για την επόµενη εντολή. Βασικά το σύστηµα ήταν λίγο πιο πολύπλοκο αλλά το απλοποιώ τώρα για να πάρουµε µια γενική ιδέα. Αν κάποιος θέλει να βουτήξει στα βαθιά µπορεί να δει στο hardware manual της Amiga και πιο συγκεκριµένα την ανάλυση µιας raster line και θα καταλάβει ότι βασικά το παιχνίδι παίζεται εκεί. Όλο το σύστηµα χρονισµού της Amiga επικεντρώνεται στις raster lines αλλά είπαµε όχι άλλες δύσπεπτες λέξεις. Αλλά έτσι για να σας κεντρίσω το ενδιαφέρον να σας πω ότι όσο πιο υψηλή ανάλυση χρησιµοποιούσε κάποιος τόσο πιο υψηλό µπορούσε να ήταν το sample playback rate της Paula. Υπό φυσιολογικές συνθήκες η Paula είχε πάνω όριο τα 22Khz, στο ECS όµως µπορούσε να ανέβει και παραπάνω γιατί η raster line ήταν µεγαλύτερη οπότε είχε για περισσότερο χρόνο στη διάθεση της τη µνήµη !
Ξαναγυρνάµε στην εταιρία Amiga η οποία είχε αρχίσει να βγάζει παιχνίδια για το 2600 αλλά και διάφορα περιφερειακά µε πιο γνωστό το joyboard που µπορούµε να πούµε ότι ήταν ο πρόγονος του wii fit.
Οι τύποι είπαµε βλέπανε µπροστά, πολύ µπροστά. Η κονσόλα που θα φτιάχνανε έπρεπε να έχει ένα όνοµα και τότε ήταν της µόδας να δίνουν στα διάφορα projects ονόµατα γυναικών. Lorraine ήταν το όνοµα της γυναίκας του προέδρου οπότε Lorraine ονοµάσανε και το δηµιούργηµα τους. Για να ξεχωρίζουν τα διάφορα chips µεταξύ τους αλλά και τις διάφορες εκδόσεις αυτών άρχισαν να δίνουν και σε αυτά γυναικεία ονόµατα. To chip του ήχου για παράδειγµα λεγόταν Portia και µετά από τη πρώτη έκδοση πήρε το όνοµα Paula, το οποίο ήταν το όνοµα της γυναίκας του τύπου που έφτιαξε το chip. Daphne ήταν το όνοµα του chip των γραφικών, το οποίο µετέπειτα µετονοµάστηκε σε Denise.
Η Lorraine λοιπόν η γυναίκα του προέδρου ήταν πολύ χαρούµενη που δώσανε το όνοµα της στο πνευµατικό παιδί τους. Αυτό µέχρι ένα βράδυ που πέρασε από το χώρο που ήταν οι προγραµµατιστές και τους άκουσε να πλαισιώνουν µε όµορφους χαρακτηρισµούς το ονοµατάκι της ... Βλέπετε στα αρχικά στάδια υλοποίησης η Lorraine ήταν πολύ ... ευαίσθητη και χάλαγε εύκολα. Το όλο κύκλωµα ήταν σε ένα χώρο που ίσα-ίσα χώραγε ένα άτοµο µε ειδικά αντιστατικά υλικά παντού µιας και η παραµικρή υποψία στατικού ηλεκτρισµού ήταν καταστροφική. Για να σας δώσω µια καλύτερη εικόνα του πόσο ευαίσθητη ήταν η lorraine θα σας πω το εξής.
Όταν τη µεταφέρανε στην έκθεση πληροφορικής τον Ιανουάριο του 1984 για να την δείξουν στους υποψήφιους αγοραστές την είχαν σε ξύλινα κιβώτια και αυτά µε τη σειρά τους τα είχαν περιτριγυρισµένα µε µαξιλάρια στο χώρο επιβατών του αεροπλάνου σε κανονική θέση. Η Lorraine όταν ταξίδευε έβγαζε κανονική θέση επιβατών και αριστερά – δεξιά της είχε ανθρώπους που τη κρατάγανε. Κάθε θέση επιβατών πρέπει να είναι καταγεγραµµένη ώστε σε περίπτωση ατυχήµατος να γνωρίζουν ποιος καθόταν εκεί. Έτσι δηµιουργήθηκε ο θρυλικός Joe Pillow, όταν οι R.J Mical και Dave Luck βάζανε ένα κουστούµι στις κούτες και µια χαρτοσακούλα για κεφάλι στο πάνω µέρος και τον δηλώνανε κανοινικά σαν επιβάτη του αεροπλάνου! Ο Joe Pillow έχει και αυτός βάλει την υπογραφή του στο κουτί της Amiga 1000!

Είπαµε η πολύ δουλειά είναι επιβλαβής στην ψυχική υγεία µας και όταν µιλάµε για πολύ δουλειά µιλάµε για ενενήντα µε εκατό ώρες την εβδοµάδα µε όλη την οµάδα να ζει ουσιαστικά µέσα στο γραφείο στην Santa Clara. Ο µοναδικός ύπνος που είχανε ήταν κατά τη διάρκεια του compilation. Οι προγραµµατιστές είχαν µαξιλάρια στα πόδια τους στα οποία ακουµπάγανε και κοιµόντουσαν για 5 µε 10 λεπτά κάθε φορά που κάνανε compilation. Οι υπολογιστές που χρησιµοποιούσαν για να γράψουν το software και τη λειτουργικό της Amiga ήταν SAGE. Τίποτα το ιδιαίτερο απλά ήταν φθηνοί και µπορούσαν να δουλέψουν και δέκα άτοµα πάνω σε έναν SAGE, ο οποίος όµως ήταν σχεδιασµένος για τέσσερα άτοµα max! O άµοιρος Sage λοιπόν µε τα 128ΚΒ µνήµης του και µε έναν 68000 στα 8Mhz τράβαγε τα πάνδεινα αλλά δεν υπήρχε κανένας άλλος τρόπος να αναπτύξεις τότε λογισµικό. (*) Πόσο µάλλον όταν δεν είχες και το hardware στα χέρια σου, µιας και η Lorraine κάθε άλλο παρά έτοιµη ήταν. Οι φίλοι µας για να επιταχύνουν την ανάπτυξη του λειτουργικού δηµιούργησαν έναν emulator κάτι σαν τον σηµερινό UAE. Με τα specifications που τους έδιναν ο Miner και οι υπόλοιποι αρχιτέκτονες των chipset φτιάξανε µια software Lorraine και εκεί πάνω αρχίσανε και χτίζανε το λειτουργικό σύστηµα. Η βάση του λειτουργικό φτιάχτηκε από τον Carl Sassanath ο οποίος δούλευε στην HP πιο πριν και επίσης είχε φτιάξει το γραφικό περιβάλλον στα Sun workstations που άρχιζε να κατασκευάζει ένας συµµαθητής του από το κολέγιο (µικρός που είναι ο κόσµος). Ο Carl δεν µάσαγε, κονσόλα παιχνιδιών, υπολογιστής δεν έχει σηµασία τι θα καταλήξει να είναι, η Lorraine έπρεπε να έχει multitasking λειτουργικό σύστηµα. Και όχι µόνο multitasking αλλά preemptive multitasking που σηµαίνει ότι ο scheduler του λειτουργικού που είναι υπεύθυνος για την χρονοδροµολόγιση των διεργασιών αναπροσαρµόζει συνέχεια τις προτεραιότητες που δίνει. Να σας πω µόνο ότι τα Windows δεν είχαν τέτοια χαρακτηριστικά ούτε µια δεκαετία µετά, πόσο µάλλον το MS-DOS που υπήρχε τότε ή το MacOS.
Μερικά χρόνια αργότερα η Apple προσέλαβε τον Sassanth για να μετατρέψει το Legacy MacOS σε multitasking αλλά αυτό ήταν αδύνατο, έπρεπε να ξαναγραφεί από την αρχή για κάτι τέτοιο όπως και πράγματι έγινε.
Πάνω από το Exec κοµµάτι του λειτουργικού υπήρχαν διάφορα άλλα layers ένα από αυτά ήταν και το γραφικό περιβάλλον που το έφτιαξε ο R.J Mical. Εκείνη την εποχή όπως και σήµερα οι µηνύσεις για τις πατέντες έπεφταν βροχή, οπότε έπρεπε να φτιαχτεί κάτι που δεν θα είχε κανείς άλλος για να µην δεχτούν καµιά µήνυση από την Apple για παράδειγµα. Έτσι και έγινε, ποτέ και κανένας δεν έκανε µήνυση για το Intuition τις γραφικές βιβλιοθήκες της Amiga ενώ ακόµα και µέχρι και πριν λίγα χρόνια η Microsoft καταπατούσε κάποιες από τις πατέντες που είχε κατοχυρώσει ο Mical!
Ένα θέµα υπήρχε µε το κοµµάτι του λειτουργικού που αφορούσε την διαχείριση των δίσκων. Επειδή δεν προλαβαίνανε να το γράψουν το είχαν αναθέσει σε εξωτερικό συνεργάτη. Όταν είδαν τα πρώτα δείγµατα της δουλειάς τους οι προγραµµατιστές της Lorraine ενθουσιάστηκαν. Οι µηχανικοί της εταιρίας αυτής είχαν κάνει εξαιρετική δουλειά και το τελικό αποτέλεσµα που θα είχαν στα χέρια τους µετά από µερικούς µήνες θα “κούµπωνε” ωραία πάνω στο EXEC µέρος του λειτουργικού. Οι κακοί Managers όµως ήταν γενικό φαινόµενο εκείνη την εποχή και όταν είδαν ότι η Lorraine γινόταν κάτι πολύ καλό και ότι θα υπήρχαν αρκετά “φράγκα” στην ιστορία ανέβασαν την τιµή σε τόσο υψηλό σηµείο που δεν µπορούσαν να κάνουν τίποτα οι άνθρωποι του Miner από το να αποτανθούν αλλού για το κοµµάτι της διαχείρισης των δίσκων. Έτσι έκαναν το λάθος και ενσωµάτωσαν το TRIPOS της Metacomco ένα τραγικό DOS που ήταν γραµµένο σε BCPL Οι προγραµµατιστές έριξαν πολύ δουλειά για να το φέρουν στα µέτρα τους αλλά το αποτέλεσµα ήταν και πάλι οικτρό. Από την έκδοση 2.0 και µετά βελτιώθηκαν λίγο τα πράγµατα µιας και ξαναγράφτηκε σε C αλλά και πάλι το χάλι-χάλι. Στο AmigaDOS 4.0 έφυγε και η τελευταία γραµµή κώδικα του TRIPOS αλλά τι να το κάνεις το κακό είχε γίνει. Πόσο θα βελτιωθεί ο κόσµος όταν εξαλειφθεί και ο τελευταίος manager από το πρόσωπο της γης άραγε.. Θεωρητικά ο Miner και οι άνθρωποι του θα έπρεπε να είναι τρισευτυχισµένοι µε την επιτυχία τους. Είχαν φτιάξει κάτι που σε τεχνική βάση δεν είχε αντίπαλο και όλα φαινόντουσαν τέλεια µέχρι τη στιγµή που άρχισαν να τελειώνουν τα χρήµατα των οδοντιάτρων. Ο Miner έβαλε υποθήκη το σπίτι του και έπρεπε άµεσα να κάνουν κάτι. Τότε ήταν που το κοράκι ο Jack Tramiel εµφανίστηκε από το πουθενά και ξεκίνησε µε την Amiga διαπραγµατεύσεις για την αγορά της τεχνολογίας της. Ο σκοπός του ήταν να φτιάξει τον C64 killer. Εκείνον το καιρό ο Jack είχε απολυθεί από την Commodore και έψαχνε έναν τρόπο να την χτυπήσει οπότε είχε φτιάξει την εταιρία Tramel (χωρίς το ι). Το κακό µε τον Jack ήταν η τσιγκουνιά του και τα νεύρα του. ∆εν ήξερε πότε πρέπει να σταµατήσει αλλά και πόσο κάνει το κάθε τι στην αγορά πληροφορικής. Όταν ξεκίνησαν τις διαπραγµατεύσεις µε τον Dave Morse αντιπρόεδρο της Amiga η πρόταση του Tramiel ήταν στα 3 δολάρια. Όταν έµαθε τις οικονοµικές δυσκολίες της Amiga σαν εταιρία έριξε την προσφορά του στα 98 cents !!! Η τιµή ήταν τόσο χαµηλή που το τυροπιτάδικο απέναντι θα κόστιζε πιο ακριβά. Ήταν φυσικό να µην υπάρξει συµφωνία και έτσι έχασε την ευκαιρία να αγοράσει την Amiga σε πολύ καλή τιµή γιατί αν τους έλεγε τότε 4 δολάρια τη µετοχή θα είχαν πει το ναι οι άνθρωποι της Amiga! Τέλος πάντων καλύτερα για µας µάλλον που δεν έκατσε αυτό το deal. Ωστόσο για να συνεχίσουν να πληρώνονται οι µηχανικοί έπρεπε να γίνει κάτι και έτσι έκλεισε µια συµφωνία µε την Atari για να της δώσει την άδεια να χρησιµοποιήσει τα Chips της. Η συµφωνία έκλεισε στις 23 Νοεµβρίου του 83. Η Atari θα είχε δικαίωµα να χρησιµοποιήσει την νέα τεχνολογία σε µια νέα κονσόλα µε τον κωδικό Mickey για έναν χρόνο. Μετά από αυτό το διάστηµα θα µπορούσε να πουλάει πληκτρολόγιο για την κονσόλα ώστε να την κάνει κάτι σαν υπολογιστή αλλά και να βγάλει έναν υπολογιστή µε την ονοµασία 1850XLD. Τα χαρακτηριστικά του Mickey ήταν τα παρακάτω:

Has a Motorola 68000 Main Processor and 128K bytes of random Access memory.
Has a detachable keyboard. lable sound generators. Each of these generators may be programmed to produce a wide variety of tones and kinds of waveforms. Many different kinds of musical effects are possible.
Can produce 40 columns by 25 lines of text
Can produce 80 columns by 25 lines of colored text on a video monitor without adding any extra cost 80-column cards.
Can mix multi-colored graphics and muiti-colored text on the same display.
Can produce dozens of easily controlled multi-colored moveable objects called "sprites" on the screen
Can define one or two indepently moveable normal or hich, resolution graphics planes called "playfields".
Up to 4096 color choices for each picture element for normal resolution playfields, or up to 16 color choices for each picture element for a high resolution playfield.
Can define whether some of the sprites are more important than some of the playfields, so that if one object is supposed to be "in front of" another, it will appear that way on the display. It also can sense and report collisions between the sprites and the playfields or between individual sprites. Can rapidly move and color-fill graphic shapes.
Has a special-effects coprocessor which can produce multiple part way-through-the-display changes in the system operating mode.

Η Atari από τη μεριά της έδωσε 500000 $ για να μεταφέρει στο πυρίτιο η Amiga τα custom chips και στο τέλος του Ιουνίου του1984 θα έπρεπε να ξεκινήσουν οι διαπραγματεύσεις για το κόστος της μετοχής ή να γυρίσουν τα 500000 πίσω. Η πρόοδος με το chipset ήταν εμφανής πια στις αρχές του 1984 όταν η Lorraine μετακόμισε για λίγες μέρες στην μεγαλύτερη έκθεση υπολογιστών των ΗΠΑ. Το περίπτερο της Amiga ήταν λίγο διαφορετικό από τα υπόλοιπα. Βασικά ήταν ένας χώρος στον οποίο είχε πρόσβαση ο κόσµος που µπορούσε να δει τα προϊόντα της εταιρίας για το 2600 και υπήρχε και ένα δωµάτιο στο οποίο πήγαινε κάποιος µόνο συστηµένος. Εκεί έµπαιναν όσοι ενδιαφέρονταν να συνεργαστούν µε την εταιρία Amiga για να προωθήσουν την τεχνολογία της Lorraine οπότε κανείς κοινός θνητός δεν µπορούσε να δει το πρωτότυπο αλλά όλοι άκουγαν έναν περίεργο ήχο από µέσα σαν κάποιος να χτύπαγε µια γκαραζόπορτα µε ένα ρόπαλο του µπέιζµπολ και επιφωνήµατα του στυλ “Oh shit”. Οι τυχεροί που έβλεπαν το περιβόητο σήµερα Boing demo µένανε µε το στόµα ανοιχτό, άλλοι ψάχνανε πίσω από τις χύµα πλακέτες της Lorraine να δουν που πάνε τα καλώδια της οθόνης µη µπορώντας να πιστέψουν ότι αυτό που βλέπουν είναι ένα demo πραγµατικού χρόνου. Το τροµερό µε το boing δεν ήταν µόνο το ότι η µπάλα είχε οµαλό animation αλλά το ότι εκείνη την ώρα που το έβλεπαν κάποιος από τους προγραµµατιστές που ήταν πάντα παρόν άνοιγε και έναν επεξεργαστή κειµένου µαζί ή κάποιο άλλο demo για να κάνει επίδειξη των multitasking δυνατοτήτων της Lorraine .
Αρχές του Ιουνίου η Lorraine µε τη βοήθεια του Joe Pillow ξαναπήγε στην έκθεση πληροφορικής αυτή τη φορά όχι µε τη µορφή χύµα πλακετών αλλά µε chips!.
Εκεί ο Dave Morse έμαθε ότι ο Jack Tramiel εξαγόρασε την Atari που ήθελε να ξεφορτωθεί η Warner με το εντυπωσιακό νούμερο των.... μηδέν δολαρίων!
Έπρεπε πάση θυσία να βρεθεί αγοραστής για την Lorraine γιατί το τέλος Ιούνη ήταν κοντά. Την Lorraine την είδαν πολλοί εκπρόσωποι εταιριών μεταξύ άλλων η Sony, η SGI, και η HP. Το θεικό το είχε πει ο Steve Jobs.
“Αυτός ο υπολογιστής έχει πολύ hardware”. Δηλαδή τι ήθελε να γίνουν όλα αυτά με κάποιον μαγικό τρόπο?
Μη μπορώντας να βρούνε αγοραστή κάνουν κάτι αδιανόητο. Βγάζουν τον 68000 και βάζουν Intel 8088 (!). Η Amiga έπρεπε να ευνουχιστεί και να γίνει PC συμβατή μήπως και την αγοράσει κάποιος που θέλει να την προωθήσει σαν επαγγελματικό υπολογιστή.
Τότε ήταν που ήρθαν οι άνθρωποι της Commodore που ενδιαφέρονταν και αυτοί για το chipset του διάδοχου του C64. Και αυτοί δεν θέλανε τίποτα άλλο πλην την τεχνολογία της Lorraine.
Αυτό μέχρι που το κακό τους χτύπησε. Ο Jack πήρε σχεδόν όλους τους τεχνικούς και τους managers της Commodore στην Atari. Η Commodore είχε ξεμείνει από ανθρώπινο δυναμικό. Και να παίρνανε τα chips δεν είχαν τι να τα κάνουν γιατί δεν είχαν πια παρά ελάχιστους τεχνικούς να τα κάνουν έναν ολοκληρωμένο υπολογιστή. Ο Jack είχε κάνει την Commodore μεγάλη και τρανή και με την έξοδο του είχε πέσει η μετοχή της από τα 60$ στα 20$. Αυτό έδειχνε ότι η χρηματιστηριακή αγορά μπορεί να μην ήξερε πόσο ανίκανος ήταν ο Tramiel για να οδηγήσει μια εταιρία υψηλής τεχνολογίας αλλά σίγουρα μπορούσε να ανεβάζει την τιμή μιας μετοχής. Οι τεχνικοί της Commodore είδαν στο πρόσωπο του Tramiel τον άνθρωπο που πήρε μια εταιρία κατασκευής calculators και την έκανε εταιρία μεγάλου κεφαλαίου και έτσι πήγαν στην Atari.
O Irving ο γενικός διευθυντής της Commodore ο άνθρωπος που είχε απολύσει τον Tramiel μη ξέροντας τι να κάνει αγόρασε την εταιρία Amiga ολόκληρη μαζί με τους ανθρώπους της ώστε να καλύψει το κενό και επίσης έκανε μήνυση στον Tramiel ότι πήρε σχέδια και documentation μαζί με τους ανθρώπους/ Μάλιστα οι εισαγγελείς σταματήσανε τα vans που μεταφέρανε τα πράγματα των εργαζομένων της Commodore που την “κάνανε” και ήταν τίγκα στα έγγραφα με το λογότυπο Commodore.
O Tramiel πυρ και μανία έψαχνε τρόπο να χτυπήσει την πρώην του εταιρεία και το βρήκε ψάχνοντας τα projects που είχε στο συρτάρι η Atari πριν αναλάβει αυτός.
Τότε ήταν που βρήκε το Mickey και ...άρχισε ο πόλεμος... βασικά τα δικαστήρια, το τέλος των οποίων ήρθε το 1987 χωρίς να υπάρχει κανείς νικητής ή μάλλον και οι δύο πλευρές να δηλώνουννικητές...τρέχα γύρευε δηλαδή.
Επί τη ευκαιρία να πούμε λίγα λόγια για την τότε Atari μιας και θα ήταν αργότερα ο κυριότερος αντίπαλος.
Η Warner Communications ήθελε καιρό να ξεφορτωθεί την Atari και ήθελε βασικά να την δώσει στον Tramiel. Δύο μέρες κράτησαν οι διαπραγματεύσεις και ο Tramiel έφυγε από το conference room με μια εταιρία σαν την Atari δώρο,
Ο Triamiel ήθελε να βγάλει έναν υπολογιστή και να ρευστοποιήσει όλα τα υπόλοιπα περιουσιακά στοιχεία της Atari. Έτσι θα έδινε πίσω τα 200 κάτι εκατομμύρια δολάρια που ήθελε η Warner σε έναν χρόνο.
Ο Tramiel έβαλε τους δύο γιους του αμέσως επικεφαλής στην εταιρία. Η φήμη του Tramiel τον ακολουθούσε παντού. Μάλιστα όταν μπήκαν για πρώτη φορά στο κτήριο της Atari ακούστηκε από το μεγάφωνο το εξής:
“Emperial troopers face been borded” !!!!
Οι δύο Tramiels πήραν πεντάλεπτες συνεντεύξεις από όλο το προσωπικό και αφού τους φώναξαν σε ένα σημείο που μπορούσαν να ακουστούν καλύτερα διάβασαν δύο λίστες. Η μεγάλη στην οποία ήταν τα 2/3 του προσωπικού αφορούσε τα άτομα που θα μάζευαν τα μπογαλάκια τους. Στην μικρή λίστα ήταν οι άνθρωποι που θα έμεναν και παρέα με τους υπαλλήλους που θα ερχόντουσαν σε λίγες μέρες από την Commodore θα σχεδίαζαν έναν υπολογιστή Sixteen Thertytwo bits ή αλλιώς τον ST.
O Tramiel ήταν πάντα της άποψης ότι δεν χρειάζονται χρήματα αλλά απλή και ωμή πίεση στους υπαλλήλους. Το χρονοδιάγραμμα ήταν απλό, σε οχτώ μήνες έπρεπε να υπάρχει έτοιμο το προϊόν αλλιώς κάποιοι θα είχαν πρόβλημα. Και έτσι έγινε, όχι αν θέλανε ας κάνανε αλλιώς.!
Ο Tramiel μην έχοντας μεγάλα έξοδα έρευνας ανάπτυξης και κατασκευής έβγαλε έναν υπολογιστή που ήταν πολύ φθηνός. Για κάθε μια Amiga 1000 που πουλιόταν, εννέα Atari ST είχαν ήδη πουληθεί.
Ξαναγυρίζοντας πίσω στην Commodore ο Miner και η παρέα του είχαν δύσκολο έργο. Έπρεπε να βγάλουν τον flat line νεκρό τεχνολογικά 8088 από την Lorraine και να ξαναγυρίσουν στην προηγούμενη σχεδίαση με τον 68000. Επίσης έπρεπε να πείσουν τους managers της Commodore ότι ο υπολογιστής πρέπει να έχει τουλάχιστον 512ΚΒ μνήμης. Αυτό ήταν όμως σχεδόν αδύνατο μιας και η μνήμη ήταν ακριιβή και οι managers καμένα χαρτιά όπως είπα (τόσες φορές).
Η Amiga βγαίνει στην κυκλοφορία με τυμπανοκρουσίες. Ήταν πράγματι ότι καλύτερο υπήρχε αλλά ένα σοβαρό πρόβλημα αμαύρωνε την εικόνα της .

Η συνέχεια κάποια άλλη στιγμή.


(*) Η ανάπτυξη της ROM του Atari ST είχε γίνει σε Apple Lisa
αλλά αυτό ήταν 1-2 χρόνια αργότερα.



Read more ...

Wednesday, August 26, 2009

iPhone Coding Part3

Σε αυτό το post θα ρίξουμε μια ματιά στο Core Animation Framework του iPhone αλλά και του MacOS μιας και είναι σχεδόν ίδια. Βασικά το ένα είναι υποσύνολο του άλλου.
Το Core Animation μας λύνει τα χέρια, είναι μια πολύ εύκολη βιβλιοθήκη σχεδιασμένη για διανοητικά καθυστερημένους προγραμματιστές σαν και και τον γράφοντα.
Αυτό μου αρέσει με το MacOS η απλότητα του, όλα γίνονται σχεδόν με μαγικό τρόπο. Για όσους έχουν δοκιμάσει να γράψουν παιχνίδι σε άλλες πλατφόρμες (win32, SDL, gtk) θα είδαν ότι δεν όλα τόσο ρόδινα, εδώ όμως είναι!
Η Core Animation έχει το concept των Layers τα οποία είναι βασικά ορθογώνια παραλληλεπίπεδα υποστηριζόμενα από την OpenGL.
Θα μπορούσαμε να το κάνουμε και εμείς εύκολα με την OpenGL (βασικά θα το δούμε πως γίνεται σε άλλο tutorial) άλλα οι άνθρωποι προσπαθούν να μας κάνουν τη ζωή ακόμα ποιο εύκολη γιατί να τους γειώσουμε :)
Ο κώδικας που χειρίζεται τα Layers είναι πολύ απλός. το μόνο που πρέπει να θυμάστε είναι ότι οι συντεταγμένες τους είναι το κέντρο του Layer εκτός και αν το αλλάξουμε που δεν θα το κάνουμε εδώ.
Και δεν θα το κάνουμε γιατί σε λίγο που θα αρχίσουμε να περιστρέφουμε τα Layers θα έχουν σαν pivot point το κέντρο τους.
Με τη μέθοδο position τοποθετούμε το Layer στον χώρο μας σε ένα συγκεκριμένο σημείο (CGPoint). Όπου CG = Core Graphics, μια άλλη βιβλιοθήκη.
Με τη μέθοδο bounds ορίζουμε το μέγεθος και εδώ χρησιμοποιούμε την CGRectMake όπου φτιάχνουμε το τετράγωνο μας, αφού έχουμε ίδιες πλευρές μεγέθους objectSize.
Με την μέθοδο contents ορίζουμε το τι θα περιλαμβάνει αυτό το Layer. Εδώ έχουμε πολυ κουβέντα αλλά προς το παρόν θα βάλουμε μια εικόνα PNG.
H zPosition δε νομίζω να χρειάζεται επεξήγηση, το λέει το όνομα της. Είναι η θέση που έχει το Layer στο zBuffer των Layers του View στο οποίο παίζουμε.
Όσον αφορά το business logic του παιχνιδιού μας, τα tileLayer arrays είναι arrays από CALayers
Tα layerX και layerΥ έχουν τις συντεταγμένες τους, τα tileAngle έχουν τις γωνίες περιστροφής και πάει λέγοντας. Θεωρώ ότι ο κώδικας όσο κακογραμμένος και να είναι έχει εύκολα ονόματα μεταβλητών για να καταλάβουμε τι παίζει.
Λοίπουν κάποιες ρουτίνες θα τις ανεβάζω σιγά-σιγά για να μη χαθούμε




-(void)setupTIle:(int)_tileID:(int)_x:(int)_y:(id)_imageRef:(float)_z:(int)_posInMapStruct:(BOOL)_storeAngleInArray:(BOOL)isTeleport
{
tileLayer[_tileID] = [CALayer layer];
tileLayer[_tileID].position=CGPointMake(_x+objectHalfSize,_y+objectHalfSize);
tileLayer[_tileID].bounds=CGRectMake(0,0,objectSize,objectSize);

tileLayer[_tileID].contents = _imageRef;
tileLayer[_tileID].zPosition=_z;
layerX[_tileID]=_x;
layerY[_tileID]=_y;
initialTileCoordX[tileID] = _x;
initialTileCoordY[tileID] = _y;
if (_storeAngleInArray) {
tileAngle[_tileID]=level[0].rot[_posInMapStruct];
if (tileAngle[_tileID] !=0)
[self rotateTile:tileID:tileAngle[_tileID]:YES];
}
if (isTeleport)
tileAngle[tileID]=level[0].rot[_posInMapStruct];
[rootLayer addSublayer:tileLayer[_tileID]];

}
-(void)setupTileLayers{
tileID = -1;
int posInMapStruct = 0;
for (int rowNum=0; rowNum < level[0].rowsInMap ; rowNum++){
for (int columnNum=0 ; columnNum < level[0].objectsPerRow ; columnNum++){
tileID++;
x = (columnNum * objectSize);
y = (rowNum * objectSize) ;

posInMapStruct = (rowNum * level[0].objectsPerRow) + columnNum;

if (level[0].map[posInMapStruct] == 0) {
layerX[tileID]=x;
layerY[tileID]=y;
initialTileCoordX[tileID] = x;
initialTileCoordY[tileID] = y;
}
else
{
switch ( level[0].map[posInMapStruct] )
{
case 0: break;
case MIRROR:
[self setupTIle:tileID:x:y:(id)mirrorImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case DOUBLE_MIRROR:
[self setupTIle:tileID:x:y:(id)doubleMirrorImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case GATES:
[self setupTIle:tileID:x:y:(id)gatesImageRef:.6f:posInMapStruct:DO_NOT_STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
[self animateGate:tileID];
break;
case TELEPORT:
[self setupTIle:tileID:x:y:(id)teleportImageRef:.6f:posInMapStruct:DO_NOT_STORE_ANGLE_IN_ARRAY:TELEPORTATION];
break;
case Y_TILE:
[self setupTIle:tileID:x:y:(id)YImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case PRISM:
[self setupTIle:tileID:x:y:(id)prismImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case RED_FILM:
[self setupTIle:tileID:x:y:(id)redImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case GREEN_FILM:
[self setupTIle:tileID:x:y:(id)greenImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case LAZER:
[self setupTIle:tileID:x:y:(id)lazerImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];

//Calc Lazer Beam begining
[self addNewLazerBeam:tileID:x:y:0:YES];
break;
//Blocks
case BLOCK_1_FULL:
[self setupTIle:tileID:x:y:(id)block1FullImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
}
}
}
}
}


-(void)showLazer{
[tileLayer[LAZER_LAYER_ID] setNeedsDisplay];
}

-(void)setupLevelIndicatorLayer{
}
-(void)calcScoreLayerCoordinates{
visibleRect = tileLayer[0].visibleRect;
scoreLayerCoordinates = CGPointMake( visibleRect.size.width, visibleRect.size.height+300); //FIX HARD VALUE LATER
}
-(void)setupScoreIndicatorLayer{
tileLayer[SCORE_LAYER_ID]= [CALayer layer];
self.calcScoreLayerCoordinates;
tileLayer[SCORE_LAYER_ID].bounds = CGRectMake(scoreLayerCoordinates.x, scoreLayerCoordinates.y, screenWidth, 20); //FIX HARD VALUE LATER
tileLayer[SCORE_LAYER_ID].position=CGPointMake(scoreLayerCoordinates.x+(screenWidth/2), scoreLayerCoordinates.y+310);//FIX HARD VALUE LATER
tileLayer[SCORE_LAYER_ID].zPosition=4.;
[rootLayer addSublayer:tileLayer[SCORE_LAYER_ID]];
}

-(void)setupLevel {
self.backgroundColor = [UIColor colorWithPatternImage:image];
rootLayer = [self layer];
[self setupTileLayers];
[self setupRotorLayer];
[self setupLazerBeams];
[self showLazer];
}



Read more ...

Sunday, August 23, 2009

iPhone Coding Part2

Η πρώτη μας κλάση είναι η "Delecaτη" μας. Πολύ απλή η λειτουργία της μιας και το μόνο μόνο που έχει να κάνει είναι να δημιουργήσει ένα instance του View που θα χρησιμοποιήσουμε να το γυρίσει 90 μοίρες ώστε το παιχνίδι μας να γίνει Landscape και εξαφανίσει την μπάρα πάνω-πάνω.
Στα γρήγορα τα δύο αρχεία μας θα μοιάζουν κάπως έτσι:

To .h...

#import <UIKit/UIKit.h>

@class DeflectorViewController;

@interface DeflectorAppDelegate : NSObject <UIApplicationDelegate, UIScrollViewDelegate> {
IBOutlet UIWindow *window;

}

@property (nonatomic, retain) UIWindow *window;

@end

είναι πολύ απλό, δεν έχουμε αλλάξει και πολλά εκτός από το πρωτόκολλο (θα τα μάθουμε στα Objective-C tutorials) για την UIScrollViewDelegate.
Η @class γνωστοποιεί στον compiler ότι χρειαζόμαστε και την DeflectorViewController. Θα μπορούσαμε να κάνουμε ένα απλό #import αλλά αυτό είναι πιο λειτουργικό:)
To IBOutlet είναι κάτι το αδιάφορο, υπάρχει απλά και μόνο για να μπορεί ο Interface Builder να "δει" την μεταβλητή window. Μιας και δεν θα χρησιμοποιήσουμε πολύ τον Interface Builder σε αυτό το παιχνίδι το προσπερνάμε.
Μιας και είπαμε IB πρέπει κάποια στιγμή να πάμε να ορίσουμε τον ViewController...ελπίζω να το θυμηθώ :)
Το @property δημιουργεί getters και setters για την Window. Βασικά η synthesize το κάνει αυτό αλλά εδώ δηλώνουμε την πρόθεση μας.

Το .m:

#import "DeflectorAppDelegate.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreGraphics/CoreGraphics.h>
#import "MainView.h"
#define degreesToRadians(x) (M_PI * x / 180.0)

@implementation DeflectorAppDelegate
@class MainView;
@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {

MainView* mainView = [[MainView alloc] initWithFrame: CGRectMake(0,0,480, 320)];

mainView.transform = CGAffineTransformIdentity;
mainView.transform = CGAffineTransformMakeRotation(degreesToRadians(90));

mainView.frame = CGRectMake(0,0,320,480);
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
[window addSubview:mainView];
[window makeKeyAndVisible];
}

- (void)dealloc {
[MainView release];
[window release];
[super dealloc];
}
@end


Τίποτα τρομερό, μερικά imports, ένα macro που όπως μαρτυρά και το όνομα του μετατρέπει radians->degrees και όλη η ουσία είναι στη μέθοδο applicationDidFinish.
Οι Delecate κλάσεις είναι βοηθητικές κλάσεις. Ποιο συγκεκριμένα η DeflectorAppDelegate έχει δηλωθεί αυτόματα από τον Wizard ότι είναι η βοηθητική κλάση της εφαρμογής μας. Το iPhone ψάχνει να βρει αυτή την κλάση όταν φορτώνει το παιχνίδι μας και καλεί κάποιες από τις ρουτίνες που περιέχει.
Όταν φορτωθεί το παιχνίδι εκτελείται η μέθοδος applicationDidFinishLaunching, παλιά λέγανε αυτές τις ρουτίνες hooks.
Η δικιά μας applicationDidFinish δημιουργεί ένα νέο View στο οποίο πάνω και θα δουλέψουμε. Το View ας το δούμε σαν ένα καμβά που πάνω εκεί ζωγραφίζουμε. Μπορούμε να έχουμε πολλά views το ένα μέσα στο άλλο και να φτιάξουμε πολύπλοκα σχήματα, αρκεί να τα κάνουμε instantiate και να δηλώνουμε τον πατέρα τους.
Εμείς χρειαζόμαστε μόνο ένα. Θα σχεδιάσουμε την κλάση MainView σιγά-σιγά η οποία θα υποστηρίζει τη λειτουργία του mainView (έτσι ονομάζουμε το ένα και μοναδικό view μας).
Με την alloc κάνουμε allocate χώρο στην μνήμη και με την initWithFrame δημιουργούμε το view μας με τις διαστάσεις (480,320).
Αυτό φαντάζει λάθος μιας και η ανάλυση του iPhone είναι 320χ480 αλλά αμέσως μετά με την transform γυρίζουμε το view μας με pivot point το κεντρικό του σημείο και έτσι έρχεται "ακριβώς" με την ανάλυση που έχουμε σε Landscape.
Καλώντας την addSubView κάνουμε κύριο view το mainView στο μοναδικό παράθυρο που έχει το iPhone και αμέσως μετά το εμφανίζουμε.
Η dealloc καλείται στο τέλος για να καθαρίσει ότι έχουμε κάνει instantiate να πούμε εδώ ότι το iPhone δεν έχει τον υπέροχο Garbage Collector του MacOS, είναι ένα από τα λίγα πράγματα που αφήσανε έξω και κατά τη γνώμη μου καλά κάνανε. Το iPhone έχει μια αμεσότητα που δεν την έχουν τα υπόλοιπα τηλέφωνα, δεν χρειαζόμαστε πολλά OS threads να τρέχουν στο background.

Πάμε σε μερικά header files που θα μας βοηθήσουν να οργανώσουμε λίγο τη σκέψη μας. Πάντα έχω ένα global.h να υπάρχει, κακογραμμένο μεν, βοηθάει δε :)

#define LAZER_LAYER_ID 297
#define ROTTOR_LAYER_ID 298
#define POWER_LAYER_ID 299
#define SCORE_LAYER_ID 300

#define EMPTY_TILE 0
#define MIRROR 1
#define DOUBLE_MIRROR 2
#define PRISM 3
#define BOMB 4
#define GATES 5
#define Y_TILE 6
#define END 7
#define TELEPORT 8
#define RED_FILM 9
#define BLUE_FILM 10
#define GREEN_FILM 11

#define BLOCK_1_FULL 20
#define BLOCK_1_UP 21 //2 rows Standard up block
#define BLOCK_1_DOWN 22
#define BLOCK_1_LEFT 23
#define BLOCK_1_RIGHT 24
#define BLOCK_1_UP_LEFT_CORNER 25
#define BLOCK_1_UP_RIGHT_CORNER 26
#define BLOCK_1_DOWN_LEFT_CORNER 27
#define BLOCK_1_DOWN_RIGHT_CORNER 28
#define BLOCK_EMPTY 29

#define DANGER_WALL 50
#define DANGER_WALL_CORNER 51

#define ROTTOR 98
#define LAZER 99

#define REAL_LAZER_BEAM YES
#define BEAM_EXTENTION NO
#define START_FROM_CENTER YES
#define START_FROM_END_OF_TILE NO

#define STORE_ANGLE_IN_ARRAY YES
#define DO_NOT_STORE_ANGLE_IN_ARRAY NO

#define TELEPORTATION YES


Να θυμήσω μόνο ότι στην Objective-C το YES/NO είναι Boolean.

Πάμε να φτιάξουμε ένα άλλο header file που θα μας βοηθήσει στην ανάπτυξη. Θα λέγεται Maps.h
Κανονικά ο χάρτης θα πρέπει να είναι σε αρχείο και να φορτώνεται αργότερα και όχι βέβαια να είναι "καρφωτός".

static const int objectSize = 32;
static const int objectHalfSize=16;

typedef struct {
int objectsInMap;
int objectsPerRow;
int rowsInMap;
NSString* backgroundImage;
Byte map[300];
float rot[300];
} LevelStruct;

static const LevelStruct level[1]={
// Level 1
150, //objectsInMap
15, //objectsPerRow
10, //rowsInMap
@"dummyPattern.png",
{ //Objects
0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,
0,2,0,5,0,0,1,0,0,0,0,0,0,0,0, //29
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //44
0,0,0,0,1,0,0,0,11,0,0,0,0,0,0, //59
0,0,0,0,0,0,0,9,0,0,0,0,0,0,0, //74
0,0,0,0,0,0,6,0,0,0,0,0,0,0,0, //89
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //104
0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, //119
99,0,0,0,0,0,1,0,0,0,0,0,0,0,0, //134
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0
},
{ //Angles
0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,
0,0,0,0,0,0,315,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,90,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,225,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}}

Στην αρχή δηλώνουμε το μέγιστο μέγεθος του κάθε "αντικειμένου" μας. Θα ονομάσουμε τα αντικείμενα μας tiles. Tiles θα λέγονται τα τετράγωνα 32χ32 pixels που θα βρίσκονται στην οθόνη μας και θα είναι τοποθετημένα δίπλα δίπλα ανά 15άδες. Κάπως έτσι δηλαδή


Το halfsize μπορεί να σας φαίνεται χαζό αλλά αν μπορούμε να γλυτώσουμε μια διαίρεση ας τη γλυτώσουμε.
Παρακάτω φτιάχνουμε μια απλή structure που θα έχει τα βασικά για να ξεκινήσουμε και είναι όλα ευκολονόητα εκτός από τα 2 τελευταία.
Το map array 300 bytes θα είναι ο χάρτης της πίστα με τα tiles μας.
Το rot float array είναι ένα παράλληλο array που θα περιέχει τις γωνίες των tiles όπως είναι ορισμένες από τον level designer....ε οκ είναι καρφωτές προς το παρόν :)
Οι γωνίες έχουν το κλασσικό orientation με τις 0 μοίρες να είναι στα δεξιά (East) και να μετράμε counterclockwise.
Αν ορίσουμε ας πούμε το laser να "κοιτάει" προς τα πάνω, αυτό θα είναι στις 90 μοίρες. Να πω μόνο ότι το orientation έχει άμεση σχέση με το πως έχουν σχεδιαστεί τα PNGs. Ολα θα πρέπει να έχουν το ίδιο orientation.
Αμέσως δηλώνουμε μια μούφα χάρτη. Τα μηδενικά δηλώνουν ότι εκεί δεν θα υπάρχει tile (είπαμε είναι δοκιμαστικός χάρτης).
Τα υπολοιπα νούμερα αντιστοιχούν στα tiles που δηλώσαμε στο Globals.h. Θα μπορούσαμε να χρησιμοποιήσουμε εδώ ολογράφως (MIRROR, LAZER...) αλλά θα μας χάλαγε το όμορφο layout :)
Αμέσως μετά δηλώνονται και οι γωνίες, ΟΙ ΑΡΧΙΚΕΣ γωνίες.

Πάμε να γράψουμε την MainView κλαση μας τώρα. ΔΕΝ θα δώσω το .h file με τη μια γιατί είναι λίγο ζώο :) και θα το πάμε σιγά-σιγά γράφοντας πρώτα το implementation και δηλώνοντας ότι χρειάζεται στο .h.
H κλαση μας θα κάνει inherit την UIScrollView μιας και αργότερα θα προσθέσουμε scrolling ιδιότητες οπότε θα είναι κάπως έτσι

@interface MainView : UIScrollView

Όταν γίνει initialize θα εκτελεστεί η initWithFrame οπότε πρέπει να την γράψουμε :)

- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
selectedTile = -1;
lazerCount = -1;
[self lazyLoadImages:0];
[self setupTileMask:0];
[self setupLevel];
}
return self;
}

Εδώ κάνουμε initialize 2 μεταβλητές που θα χρησιμοποιοήσουμε αργότερα και εκτελούμε 3 μεθόδους.
Η LazyLoadImages παίρνει σαν παράμετρο το level number που στη περίπτωση μας έχουμε καρφωτο το 0 και φορτώνει τα PNGs που χρειάζεται. Λέγεται lazy γιατί όπως θα δείτε σε κοιτάει να δει τι χρειάζεται πραγματικά να φορτώσει μιας και κρατάει τα κοινά PNGs που έχουν τα Levels.
H setupTileMask δημιουργεί μια μάσκα πάνω από τα βοηθητικά tiles (γωνίες, blocks, τοίχοι κτλ). Η μάσκα αυτή βοηθάει στο να ξέρουμε που θα σταματήσουμε την ακτίνα μας.

/* We create a "mask" over the tiles, covering the pixels where the lazer beam stops or cannot penetrate
the array has 320 rows with 15 integers.
*/
-(void)setupTileMask:(int)_level
{ //First init array to 0s
for (int i=0 ; i<320 ; i++)
for (int j=0;j<level[_level].objectsPerRow ;j++)
blockMask[i][j] = 0;

//Scan the Map
for (int rowFirstTileIdx=0 ; rowFirstTileIdx < level[_level].objectsInMap ; rowFirstTileIdx+level[_level].objectsPerRow) //0, 15, 30, 45, 60, 75.....135
{
/* tileIdx points to

*/
int filler = 0;
for (int tileIdx = rowFirstTileIdx ; tileIdx < rowFirstTileIdx + level[_level].objectsPerRow ; tileIdx++) //Points to those 15 tiles per row
{
switch (level[_level].map[tileIdx]){ //Check Tile Type
case BLOCK_1_FULL:
for (filler=0;filler<objectSize;filler++) //Filler points to those 32 pixels hight of every tile
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFFFFFF;
break;
case BLOCK_EMPTY:
for (filler=0;filler<objectSize;filler++)
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
break;
case BLOCK_1_UP:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFFFFFF;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
break;
case BLOCK_1_DOWN:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFFFFFF;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
break;
case BLOCK_1_LEFT:
for (filler=0;filler<objectSize;filler++)
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFF0000;
break;
case BLOCK_1_RIGHT:
for (filler=0;filler<objectSize;filler++)
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0000FFFF;
break;
case BLOCK_1_UP_LEFT_CORNER:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFF0000;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
break;
case BLOCK_1_UP_RIGHT_CORNER:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0000FFFF;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
break;
case BLOCK_1_DOWN_LEFT_CORNER:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0xFFFF0000;
break;
case BLOCK_1_DOWN_RIGHT_CORNER:
for (filler=0;filler<objectHalfSize ;filler++) // Mask first 16 rowpixels
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0;
for (filler=objectHalfSize;filler<objectSize ;filler++) //Make transparent the rest
blockMask[(rowFirstTileIdx * objectSize) + filler][tileIdx] = 0x0000FFFF;
break;
}
}
}
}

-(void)loadImageNeededInLevel:(int)_imageId
:(int)_level
:(CGImageRef)_imageRef
:(NSString*)_imageFilename
{
for (tmpGlobalCounterVariable=0 ; tmpGlobalCounterVariable<level[_level].objectsInMap ; tmpGlobalCounterVariable++)
{
if (level[_level].map[tmpGlobalCounterVariable] == _imageId){
image = [UIImage imageNamed:_imageFilename];
_imageRef = image.CGImage;
}
}
}

/* Load only the images that are used in current level
DO NOT unload all the images but only those that are not used
*/
-(void)lazyLoadImages:(int)_level
{
[self loadImageNeededInLevel:MIRROR: _level: mirrorImageRef: @"mirror64.png"];
[self loadImageNeededInLevel:DOUBLE_MIRROR: _level: doubleMirrorImageRef: @"doubleMirror.png"];
[self loadImageNeededInLevel:BLOCK_1_FULL: _level: block1FullImageRef:@"BLOCK_1_FULL"];
[self loadImageNeededInLevel:BLOCK_1_UP: _level: block1UpImageRef:@"BLOCK_1_UP"];
[self loadImageNeededInLevel:BLOCK_1_DOWN: _level: block1DownImageRef:@"BLOCK_1_DOWN"];
[self loadImageNeededInLevel:BLOCK_1_LEFT: _level: block1LeftImageRef:@"BLOCK_1_LEFT"];
[self loadImageNeededInLevel:BLOCK_1_RIGHT: _level: block1RightImageRef: @"BLOCK_1_RIGHT"];
[self loadImageNeededInLevel:BLOCK_1_UP_LEFT_CORNER: _level: block1UpLeftCornerImageRef: @"BLOCK_1_UP_LEFT_CORNER"];
[self loadImageNeededInLevel:BLOCK_1_UP_RIGHT_CORNER: _level: block1UpRightImageRef: @"BLOCK_1_UP_RIGHT_CORNER"];
[self loadImageNeededInLevel:BLOCK_1_DOWN_LEFT_CORNER: _level: block1DownLeftImageRef: @"BLOCK_1_DOWN_LEFT_CORNER"];
[self loadImageNeededInLevel:BLOCK_1_DOWN_RIGHT_CORNER: _level: block1DownRightImageRef: @"BLOCK_1_DOWN_RIGHT_CORNER"];
[self loadImageNeededInLevel:LAZER: _level: lazerImageRef: @"lazer_-.png"];
[self loadImageNeededInLevel:Y_TILE: _level: YImageRef: @"Y.png"];
[self loadImageNeededInLevel:ROTTOR: _level: rottorImageRef: @"arrowRottor.png"];
[self loadImageNeededInLevel:PRISM: _level: prismImageRef: @"prism.png"];
[self loadImageNeededInLevel:BOMB: _level: bombImageRef: @"bomb.png"];
[self loadImageNeededInLevel:END: _level: endImageRef: @"end.png"];
[self loadImageNeededInLevel:GATES: _level: gatesImageRef: @"gates.png"];
[self loadImageNeededInLevel:RED_FILM: _level: redImageRef: @"red.png"];
[self loadImageNeededInLevel:GREEN_FILM: _level: greenImageRef: @"green.png"];
[self loadImageNeededInLevel:TELEPORT: _level: teleportImageRef: @"teleport.png"];

}

Ελπίζω να είναι κατανοητός ο κώδικας προσπάθησα να τον γράψω όσο πιο απλά γίνεται ώστε να τον καταλάβω όταν τον ξαναδιαβάσω 2 μήνες αργότερα μιας και το αρνητικό IQ μου δε με βοηθάει πολλές φορές :)

Το setupLevel έχει πολύ γράψιμο και να πω την αλήθεια κουράστηκα. Στο επόμενο.....
Read more ...

Sunday, August 16, 2009

Trash life


Και εκεί που έκανα zapping έπεσα πάνω σε γνωστή trash εκπομπή η οποία είχε για άλλη μια φορά φιλοξενούμενους ανθρώπους με σοβαρά προβλήματα. Όχι υγείας, ούτε εργασίας, αλλά από αυτά που θα έπρεπε για κάποιο άγνωστο λόγο να μας ενδιαφέρουν.
Δε μπορώ να καταλάβω πως εκπομπές σαν και αυτές υπάρχουν στην τηλεόραση τόσα χρόνια. Θεωρητικά ο τηλεοπτικός χρόνος κοστίζει ένα σκασμό λεφτά τα οποία πρέπει να αποσβεστούν από διαφημίσεις.
Για να βάλει κάποιος μια διαφήμιση σε trash εκπομπή σημαίνει ότι υπάρχει ένα Α ακροατήριο τόσο μεγάλο ώστε να μετράει σαν target group.
Και ερωτώ:
Εκτός από 85χρονους που νόμιζαν ότι είχαν δει τα πάντα, άλλωστε έζησαν παγκόσμιους πολέμους, πείνα, φτώχεια, εμφύλιους, χούντα και δε πίστευαν ότι μπορούν να ζήσουν ΚΑΙ αυτό, ποιος άλλος ασχολείται με αυτά τα σκουπίδια?

Υπάρχει περίπτωση κάποιος νέος άνθρωπος με "σώας τας φρένας" να κάτσει μπροστά στη τηλεόραση με σκοπό να δει μια τέτοια εκπομπή?
Υπάρχει περίπτωση κάποιος να θεωρήσει entertainment αυτές τις εκπομπές? Δηλαδή μπορεί να γελάσει κάποιος με τη προσπάθεια κάποιου να αυτογελειοποιηθεί και μάλιστα με τόσο άσχημο και άχαρο τρόπο?
Με μια γρήγορη ματιά στο Ελληνικό Internet μάλλον ναι. Για να υπάρχουν ολόκληρα sites αφιερωμένα στην Trash TV σημαίνει ότι κάποιος τα έφτιαξε και μάλλον κάποιοι κάθονται και τα διαβάζουν. Ακόμα και συνεντεύξεις σε Trash "φίρμες" είδα να παίρνουν τώρα τελευταία.

Πάντως πιστεύω ότι υπάρχει φως το τούνελ. Κάποτε ήταν γεμάτη η τηλεόραση από τραγικές φυσιογνωμίες ιδιαίτερα τα βράδια στα παρακμιακά κανάλια, αυτά που έχουμε στις τελευταίες μνήμες της τηλεόρασης μας.

Πάντα μου φαινόταν περίεργη η παρουσία αυτών των εκπομπών. Μήπως δεν υπάρχουν ιδέες από τους ιθύνοντες? Μήπως τα λεφτά από τις διαφημίσεις των απορυπαντικών είναι υπέρογκα ώστε να διατηρούν την TrashTV? Μήπως σοβαρές εκπομπές από αυτές που έχουν να προσφέρουν κάτι και όχι να καταστρέψουν τα μυαλά δεν θα έβρισκαν τον δρόμο προς τον ¨αέρα¨ λόγω έλλειψης διαφημιστών?

Δε νομίζω. Πιστεύω ότι αν κάποιος έκοβε την μια ώρα που διαρκεί μια τέτοια εκομπή και αφιέρωνε αυτό τον χρόνο στην οδική ασφάλεια στα καθημερινά προβλήματα του οδηγού για παράδειγμα, την διαπαιδαγώγηση των γονέων ώστε να μην ανεβάζουν τα παιδιά στο μηχανάκι για να πάνε για μπάνιο στη θάλασσα σίγουρα θα βρισκόταν διαφημιστής να καλύψει το κόστος.
Από την αυτοκινητιστική βιομηχανία με τα αυτοκίνητα των 18 αστέρων στο euroncap μέχρι κράνη και εξοπλισμός ασφαλείας.

Θα μπορούσε ίσως να γίνει μια σοβαρή εκπομπή πάνω στην τεχνολογία (πληροφορική, internet, gadgets, κ.λ.π). Και εκεί υπάρχει ψωμί στη διαφήμηση, από τις εταιρίες κινητής, τις αλυσίδες ηλεκτρονικών αγαθών μέχρι και μπύρες θα μπορούσες να διαφημίσεις μιας και το σπορ στην Ελλάδα είναι κυρίως θέμα ΧΥ χρωμοσωμάτων :)

Άραγε στην Ευρώπη τι παίζει? Υπάρχουν τέτοιες εκπομπές? Και αν ναι, έχουν καλύψει τον τηλεπτικό χρόνο τους πρώτα με ότι πιο χρήσιμο?
Read more ...

Saturday, August 15, 2009

Φτιάξτε με τα χεράκια σας έναν Apple I



Είναι πάνω από 30 χρόνια από τότε που ο Steve Wozniak γνωστός και ως Woz έφτιαξε τον πρώτο προσωπικό υπολογιστή.
Ήταν ένα κατόρθωμα για την εποχή. Δεν υπήρχε ένα σημείο αναφοράς, ο Woz δημιούργησε κάτι εντελώς καινούριο και καινοτόμο, το όνομα αυτού Apple I.
Μπορεί με τα σημερινά δεδομένα να φαίνεται σαν ένα αστείο μιας και η εξέλιξη της τεχνολογίας ήταν ραγδαία αλλά για την εποχή του αυτός ο υπολογιστής ήταν ότι πιο extreme υπήρχε (μιλάμε πάντα για προσωπικούς υπολογιστές).
Πως θα σας φαινόταν σήμερα να δοκιμάζατε να ακολουθήσετε τα βήματα του Woz και να κατασκευάσετε και εσείς έναν Apple I για πάρτη σας?

OK δεν θα κάνετε ότι ακριβώς έκανε ο Woz μιας και αυτό θα ήταν κομματάκι πιο δύσκολο αλλά μπορείτε να συναρμολογήσετε έναν υπολογιστή συμβατό με τον Apple I από το κιτ που πουλάει η Briel Computers.
Replica 1 λέγεται ο κλώνος ο οποίος είναι ότι πιο κοντινό υπάρχει με τον Apple I. Αν δεν τα πάτε καλά με τα κολλητήρια μπορείτε να το παραγγείλετε "δεμένο" αλλά πιστεύω ότι δεν θα το κάνετε, μιας και κρύβετε έναν μικρό woz μέσα σας.
Το θέμα είναι πως θα τον αποκτήσετε μιας και κοστίζει 150$ και έχουμε ένα θέμα με τα τελωνεία και τις εισαγωγές από τις ΗΠΑ.
Αν μπορέσει κανείς να μου φέρει ένα θα του μοντάρω και το δικό του, ε τώρα αν περισσέψει κανείς πυκνωτής ή 6502 θα τα βρούμε :)

Read more ...

Κάποια πράγματα δεν αλλάζουν

Είχα πάει για μπάνιο σήμερα σε μια από τις καλύτερες παραλίες που υπάρχουν κοντά στην Αθήνα.
Λίγος κόσμος μιας και είναι δεκαπενταύγουστος και μια θάλασσα "λάδι" που δε σου έκανε καρδιά να βγεις έξω.
Και εκεί που έλεγα πόσο τέλεια μπορεί να κυλίσει η μέρα σήμερα βλέπω 5 μέτρα μακριά από μένα ένα πατέρα να μαλώνει το παιδί του ηλικίας κάτω των πέντε.

Γενικά έχω μια ευαισθησία με τα παιδιά, πιστεύω ότι σαν ενήλικες εμείς είμαστε η πηγή των συναισθημάτων τους.
Αν μας βλέπουν στεναχωρημένους θα είναι και αυτά στεναχωρημένα, αν είμαστε θυμωμένοι και νευρικοί θα είναι και αυτά το ίδιο.
Ο πατέρας αυτός λοιπόν εκεί που φώναζε στο παιδί του, εικόνα που έχουμε ξαναδεί πολλές φορές, του δίνει μια σφαλιάρα με το βρεγμένο χέρι τόσο δυνατή που του άφησε κόκκινα σημάδια από τα δάχτυλα του στο μάγουλο για τουλάχιστον 5 λεπτά που ήμουν εκεί....
...γιατί αυτό με έκανε να μαζέψω τα πράγματα μου και να φύγω.
Γιατί?
Γιατί ήμουν νευριασμένος με εμένα που δεν είχα τα κότσια να του μιλήσω.
Γιατί δεν έκανα κάτι για το παιδάκι που θα μεγαλώσει δίπλα σε ένα πατέρα που σίγουρα θα το ξαναχτυπήσει τόσο άσχημα πολλές φορές ακόμα.
Θα μου πεις τι μπορείς να κάνεις....
Αν δεν μπορείς να κάνεις τίποτα τότε δεν έχει αλλάξει και τίποτα στους μηχανισμούς της κοινωνίας που αποτρέπουν τέτοιες συμπεριφορές από τότε που είμασταν εμείς, η γενιά των 35-40, παιδιά.
Τότε ήταν ο κανόνας, ο πατέρας να χτυπάει τα παιδιά αλύπητα, φαινόταν τόσο φυσικό μιας και το κάναν σχεδόν όλοι. Ο γονιός που δε χτύπαγε θεωρούταν ειρωνικά "προοδευτικός" και ήταν δακτυλοδεικτούμενος.
Αλλά σήμερα? Σήμερα που υπάρχει ενημέρωση? που θεωρητικά δε θα θέλαμε να κάνουμε στα παιδιά μας τα ίδια που κάνανε σε εμάς οι γονείς μας να επαναλαμβάνεται το φαινόμενο?

Από τη μια θέλω να πιστεύω ότι αυτό ήταν μια περίπτωση απομονωμένη και από την άλλη ξανασκέφτομαι το παιδάκι τι έχει να τραβήξει από αυτό τον πατέρα, και γυρνάω πάλι στο μηδέν.
Read more ...

Thursday, August 13, 2009

Objective-C (part-1)

Για να ξεκινήσουμε και να μπούμε στα ενδότερα του iPhone programming θα πρέπει πρώτα να γνωρίσουμε την Objective-C. Αυτή είναι η μόνη γλώσσα (όσο γνωρίζω τουλάχιστον) προγραμματισμού στο iPhone.
Θεωρώ ότι είναι μια από τις πιο απλές γλώσσες, είναι ιδανική για αρχάριους και πολύ εύκολη στην εκμάθηση σε όσους γνωρίζουν java (ναι java!).

O λόγος είναι ότι αν εξεραίσουμε το συντακτικό της γλώσσας οι θεμελιώδης έννοιες θυμίζουν πολύ java. Ιστορικά βέβαια η Java θυμίζει Objective-C μιας και η δεύτερη προυπήρχε.
Ο Patrick Naughton ο πατέρας της Java λάτρευε την Objective-C της τότε NeXT και μισούσε την C++. Μάλιστα ήθελε να φύγει από τη SUN και να πάει να δουλέψει στη NeXT, ευτυχώς για αυτόν που δε το έκανε μιας και θα ήταν σύντομα άνεργος! Πολλοί άνθρωποι της NeXT επίσης μετά το κλείσιμο της εταιρίας πήγαν και δούλεψαν με τον Naughton και μετέφεραν τεχνολογίες της Ojb-C όπως τα protocols στην Java (interfaces).
Αυτό που τρομάζει κάποιον που πρωτοβλέπει Obj-C είναι η λίγο περίεργη σύνταξη της. Βασικά η Obj-C είναι gcc + NeXT object oriented extentions. Μπορούμε δηλαδή να γράψουμε κανονικά C σαν να γράφαμε για το Linux μιας και compiler είναι ο ίδιος ακριβώς. Από τη στιγμή όμως που θελήσουμε να χρησιμοποίσουμε τα extentions αλλάζουμε λίγο φιλοσοφία.
Μη τρομάζετε όμως γιατί είναι ΠΟΛΥ απλά τα πράγματα.

Εν αρχή είναι η κλάση. Ο ορισμός της κλάσης και το implementation της κλάσης. Το Xcode θέλει να σπάει μια κλάση σε δύο αρχεία. Στο header file(.h) που γίνεται ο ορισμός και στο implementation(.m) που είναι το.... implementation:)
Ας δούμε ένα header file μιας κλάσης που θα χρησιμοποιήσουμε αργότερα στο παχνίδι μας. Φυσικά δε το βάζω όλο μιας και δε χρειάζεται να λιώσουμε στα listings ακόμα:)

// TestView.h
// Deflector
//
// Created by Antony Mavrelos (aka Shock) on 23/01/2009.
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "LazerBeamsLayerDelegate.h"

int beamColour[10][300];
int layerX[300];
int layerY[300];
int initialTileCoordX[300];
int initialTileCoordY[300];
int tileAngle[300];
int lazerCount;
int lazerOutX[10];
int lazerOutY[10];
int lazerPosInMap[10];
BOOL lazerOnOff[10];
int lazerAngle[10];
BOOL levelComplete;
CALayer* tileLayer[300];

@interface TestView : UIScrollView {
int lazerLayerWidth;
int lazerLayerHeight;

LazerBeamsLayerDelegate* lazerLayerDelegate;
CALayer* rootLayer;
int lazer0x, lazer0y;

CGPoint firstTouch;
CGPoint currentTouchPosition;
CGPoint scoreLayerCoordinates;
CGRect visibleRect;

BOOL levelIsDrawn;
BOOL rottorIsVisible;
BOOL firstGate;

NSInteger levelNum;
NSUInteger tapCount;

UIImage* image;
CGImageRef mirrorImageRef;
//μπλα μπλα μπλα
UITouch* touch;
int blockMask[320][30];
//μπλα μπλα μπλα
}

@property CGPoint firstTouch;
@property CGPoint currentTouchPosition;
@property CGPoint scoreLayerCoordinates;
@property CGRect visibleRect;
@property NSInteger levelNum;
@property BOOL levelIsDrawn;
@property BOOL rottorIsVisible;

- (void)rotateTile:(int)tID:(float)degree:(BOOL)setuplayers;

@end

Αυτό που μας ενδιαφέρει εδώ είναι το block

@interface TestView : UIScrollView {
//.....
@end

Αυτός είναι και ο ορισμός της κλάσης η οποία ονομάζεται TestView και κληρονομεί τις ιδιότητες της UIScrollView κλασης. Αν θέλουμε να το πάμε παραπέρα μπορούμε να ανατρέξουμε στο documentation στο XCode και να δούμε ότι και αυτή είναι παιδί της UIView και πάει λέγοντας.
Αρα με το "@interface" (σας θυμίζει τίποτα το @?:)) ξεκινάμε τη δήλωση ακολουθούμενο από το όνομα την άνω και κάτω τελεία και τον πατέρα.
Στην Obj-C όπως και στην Java δεν μπορούμε να έχουμε πολλούς πατεράδες.
Τώρα ότι βρίσκεται ανάμεσα στο @interface και @end είναι οι instance variables. Άφησα μερικές για δείγμα για να δείξω ότι μπορούμε να έχουμε κλασσικά C primitive types αλλά και άλλα αντικείμενα δικά μας (LazerBeamsLayerDelegate) ή κλασεις συστήματος όπως η UITouch.
Μιας και το έφερε η κουβέντα να πούμε ότι όταν δηλώνουμε μια μεταβλητή τύπου object πάντα μιλάμε για έναν pointer. Τα objects δεν είναι τίποτα περισσότερο από θέσεις μνήμης.
Όλες οι NSκάτι, UIκάτι κλάσεις του συστήματος χρειάζονται και το αγαπημένο μας αστεράκι:)
Π.χ UIImage* image;
Εξαίρεση αποτελεί η NSInteger η οποία βασικά είναι ένας απλός wrapper.
Αν αναρωτιέστε τι παίζει με τα CGPoint, CGRect και δεν είναι pointers η απάντηση είναι απλή, αυτές είναι structures.
Μια σύνηθες τακτική όταν γράφουμε κώδικα είναι να χρησιμοποιούμε geters και seters για τις protected μεταβλητές μας. Χωρίς να μπλέξουμε το scope των μεταβλητών στην όλη φάση και για να απλοποιήσουμε τα πράγματα να πούμε ότι το ίδιο σκεπτικό υπάρχει και εδώ αλλά με μια διαφορά. Η obj-c σου δίνει τη δυνατότητα να φτιάξεις accessors χωρίς κώδικα (τουλάχιστον όχι και τόσο γράψιμο).
Το @property κάνει αυτή ακριβώς τη δουλειά. Δηλώνουμε τη πρόθεση μας να φτιαχτούν εσωτερικά δύο μέθοδοι που μπορούμε να χρησιμοποιούμε στον κώδικα μας. Λέω "δηλώνουμε την πρόθεση μας" γιατί η πραγματική υλοποίηση γίνεται στο implementation με την @synthesize που θα δούμε αργότερα.
Η μια μέθοδος λοιπόν που φτιάχνει η Obj-C έχει το όνομα της μεταβλητής και είναι η *get* μέθοδος και η άλλη είναι η set+;"όνομα μεταβλητής".
Για παράδειγμα στην levelIsDrawn μεταβλητή θα φτιαχτει μια μέθοδος levelIsDrawn() που θα επιστρέφει BOOL και μια setlevelIsDrawn(BOOL) που θα δέχεται σαν όρισμα μια Bool τιμή, η οποία μιας και το έφερε η κουβέντα είναι YES ή NO.
Αντί για TRUE/FALSE έχουμε YES/NO, εύκολο και κατανοητό.
Στην γραμμή 61 βλέπουμε τον ορισμό μιας μεθόδου. Το "-" στην αρχή δηλώνει ότι αυτή είναι μια Instance method. Αν είχε (+) θα ήταν ο ορισμός μιας class method. Ακριβώς μετά μέσα σε παρενθέσεις είναι η επιστροφή της μεθόδου.
Ακολουθεί το όνομα και αν υπάρχουν παράμετροι τις ξεχωρίζουμε με άνω και κάτω τελείες
- (void) rotateTile :(int)tID :(float)degree:(BOOL)setuplayers;
Άρα έχουμε "instance/class sign" (return type) method name :type par1 : type par1 : type par2

Φαίνεται λίγο περίεργο για αρχή αλλά συνηθίζεται εύκολα.
Read more ...

Wednesday, August 12, 2009

Monopoly (iPhone Game Review)


Ο τίτλος φέρνει στο μυαλό μας βραδιές με φίλους, φωνές και γκρίνιες για συμφωνίες κάτω από το τραπέζι σε ανταλλαγές "καρτών" και φυσικά τα κλασσικά χαρτονομίσματα δραχμών!.
Υπάρχει και monopoly με ευρώ αλλά εμείς είμαστε retro!

Το πρώτο πράγμα που παρατηρεί κανείς όταν φορτώσει το παιχνίδι στο iPhone είναι τα πολύ καλά τρισδιάστατα γραφικά του.
Το επιτραπέζιο βρίσκεται πάνω σε ένα τραπέζι σε κάποιο όμορφα διακοσμημένο δωμάτιο ή ακόμα και σε κάποιο μπαλκόνι Ελληνικού νησιού ή τουλάχιστον έτσι θέλω να πιστεύω εγώ τουλάχιστον! Γύρω από το τραπέζι βρίσκονται οι καρέκλες των τεσσάρων παιχτών του παιχνιδιού.
Οι αντίπαλοι σας μπορεί να είναι άλλοι άνθρωποι ή ΑΙ. Μπορείτε να παίξετε με wifi ή να επιλέξετε ως τρεις ΑΙ παίχτες αν και πιστεύω το καλύτερο παιχνίδι γίνεται με τρεις συνολικά παίχτες.
Ο παίκτης μπορεί να επιλέξει να αλλάξει λίγο τους default κανόνες του επιτραπέζιου. Για παράδειγμα να χρειάζεται να έχεις χτίσει 3 σπίτια για να μπορείς να χτίσεις πολυκατοικία. Ή το free parking να μην είναι και τόσο free, το αρχικό ποσό του κάθε παίχτη, τα χρήματα που εισπράτεις όταν περνάς την αφετηρία κ.α
Το ρίξιμο των ζαριών γίνεται με κούνημα της συσκευής, ένα θέμα εδώ υπάρχει με τη μηχανή δημιουργίας τυχαίων αριθμών (rng) που χρησιμοποιεί το παιχνίδι. Αυτή είναι βασισμένη σε physics και τουλάχιστον σε μερικά παιχνίδια στην αρχή παρατήρησα επανάληψη ακολουθίας.
Ο χειρισμός των καρτών, η μετακίνηση από πόλη σε πόλη και οι δημοπρασίες χωράνε μεγάλη βελτίωση μιας και δεν είναι τόσο προφανής ο χειρισμός όταν παίζεις για πρώτη φορά. Υπάρχει ένας οδηγός που τρέχει κάθε, ΜΑ ΚΑΘΕ φορά που ξεκινάς παρτίδα (αλήθεια δεν γίνεται να σταματήσει με κάποιο τρόπο;) ο οποίος σου εξηγεί την κάθε διαδικασία αλλά αυτό δεν αλλάζει το γεγονός ότι το παιχνίδι σηκώνει αρκετές βελτιώσεις σε αυτούς τους τομείς.
Τελικό αποτέλεσμα για να μη σας κουράζω είναι ότι αν σας αρέσει η Monopoly τότε αυτό το παιχνίδι είναι must. Αν όχι δεν πρόκειται να το αγαπήσετε τώρα μιας και κάποια πράγματα ίσως σας κουράσουν, ειδικά αν παίζετε και πίνετε capuccino ταυτόχρονα:)

Συνίσταται για την πρώτη οθόνη του iPhone? Των σκληροπυρηνικών ναι!
Overall 85%

Monopoly iTunes Store Url
Developers: Venan Entertainment
κατά παραγγελία της Electronic Arts
Τιμή:3.99€
Read more ...

Tuesday, August 11, 2009

Duke Nukem 3D (iPhone Game Review)


Είχα διαβάσει εδώ και μέρες ότι θα έβγαινε ένα από τα ποιο ωραία FPS της δεκαετίας 90 στο iPhone. Ο λόγος φυσικά για το Duke Nukem 3D με τον μάτσο μαν που είχε αδυναμία στις γυναίκες ειδικά αν αυτές φοράνε μπικίνι και γνωρίζουν τις αρχαίες τεχνικές ευχαρίστησης ενός άντρα:)

Το πρόβλημα με αυτού του είδους τα παιχνίδια στο iPhone είναι ο χειρισμός τους μιας και τα εικονικά D-pads δε βολεύουν πολλές φορές. Ακόμα δεν έχω βρει ένα FPS στο οποίο να έχεις άμεσο και απόλυτο έλεγχο του χαρακτήρα, πάντα κάτι έλλειπε από τη συνταγή.
Αυτό μέχρι να δω το Duke Nukem. Πρέπει να το δει κάποιος για να το πιστέψει ότι έχεις μηδενικό σχεδόν έλεγχο του πάει πάει ο χαρακτήρας!
Μιλάμε για άλλα αντί άλλων. Μου πήρε σχεδόν ένα λεπτό για να καταφέρω να κατέβω από τη στέγη της πρώτης πίστας, διαδικασία που θέλει τρία με με τέσσερα δευτερόλεπτα στο PC!
Οι τύποι δώσανε νέο μέτρο σύγκρισης για να κρίνουμε τα παιχνίδια από δω και εμπρός.
Είχα σκοπό να κάνω review στο παιχνίδι αλλά δεν θα το κάνω μιας και είναι περισσότερο δοκιμασία νεύρων και υπομονής παρά ευχαρίστηση.
Κρίμα:(
Read more ...

Monday, August 10, 2009

Oracle Pool Connection (Java)

Μήπως δεν είστε ευχαριστημένοι από το connection pooling μηχανισμό του JBoss? Μήπως θέλετε να σκοτώσετε λίγο το χρόνο σας. Χρησιμιποιήστε το connection pooling της Oracle με τις παραμέτρους από το κλασσικό oracle-ds ή κάποιο άλλο *-ds που σας περισεύει.


import java.sql.Connection;
import java.sql.SQLException;
import java.io.*;
import java.util.Properties;
import oracle.jdbc.pool.OracleDataSource;
import org.apache.log4j.Logger;
import org.dom4j.*;
import org.dom4j.io.SAXReader; //<--Ναι θα μπορούσαμε να μη το χρησιμοποιήσουμε

public class OraclePoolDatasource{
File oracleDsFile ;
OracleDataSource ods;
Properties ps;
private org.dom4j.Document doc;

private static final String SERVER_HOME_DIR = "jboss.server.home.dir";
protected final Logger log = Logger.getLogger(OraclePoolDatasource.class.getName());

public void parseWithSAX(File dsFile) throws DocumentException {
SAXReader xmlReader = new SAXReader();
this.doc = xmlReader.read(dsFile);
}

public void createPool() {
String diployDirPath = System.getProperty(SERVER_HOME_DIR);
diployDirPath = diployDirPath.replaceAll("\\\\", "/");
final String pathname = diployDirPath + "/deploy";

File deployDir = new File(pathname);
FilenameFilter filter = new FilenameSuffixFilter("-ds.xml");
String[] oracleFileName = deployDir.list(filter);
try{
log.error("Datasource file: "+oracleFileName[0]);
oracleDsFile = new File(pathname+"/"+oracleFileName[0]);

}
catch (NullPointerException e) {
log.error("Oracle-ds file not found. Check for other -ds.xml file");
}

parseWithSAX(oracleDsFile);

Element root = doc.getRootElement();

log.error( "Database Connection URL --> "+doc.valueOf( "//datasources/local-tx-datasource/connection-url" ));

ods = new oracle.jdbc.pool.OracleDataSource();
ods.setConnectionCacheName("ShocksPool");
ods.setURL(doc.valueOf( "//datasources/local-tx-datasource/connection-url" ));
ods.setUser(doc.valueOf( "//datasources/local-tx-datasource/user-name" ));
ods.setPassword(doc.valueOf( "//datasources/local-tx-datasource/password" ));
ops = new Properties();
ops.put("MinLimit", 5);
ops.put("InitialLimit", 5);
ops.put("MaxLimit", 10);
ops.put("TimeToLiveTimeout", 360);
ods.setConnectionCacheProperties(ps);
ods.setConnectionCachingEnabled(true);
}

public void destroy() {
ods = null;
}

public Object getConnection(){
try {
if (ods!=null) {
log.info("Returning connection ");

Connection con = ods.getConnection();

con.setAutoCommit(true); //ή ότι άλλο θέλετε
if (con != null)
log.info("getConnection OK");
else
log.info("Connection creation failed");
//και σώστε τον κόσμο εδώ
return con;
}
else {
log.info("το χασαμε το datasource πατριώτη!");
return null; //και να είστε έτοιμοι για ένα ωραίο NullPointerException
}
} catch (SQLException e) {
log.info("Exception");
e.printStackTrace(); //και άντε βγάλε τα μάτια σου
return null;
}

}
}

Read more ...

Exceptions σε bulkoμπατσιές (Oracle)

Σκεφτόμουν ένα ενδιαφέρον αρθράκι για προγραμματισμό σε Oracle.
Τι καλύτερο για αρχή από βελτίωση του performance των batch διαδικασιών στα insert/update/delete και ταυτόχρονα έλεγχος του αποτελέσματος?
Με τα Pl/Sql tables τα πάμε καλά? Αν όχι πρέπει. Τα χρησιμοποιώ καθημερινά και μπορώ να πω ότι είναι πολύ βολικά. Είτε τα χρησιμοποιούμε με το OCI για να περάσουμε μαζικά δεδομένα από άλλα συστήματα, είτε καθαρά μέσα σε oracle stored procedures για επεξεργασία δεδομένων είναι τόσο εύκολα στη χρήση τους που πραγματικά λύνουν τα χέρια.

Η πιο σύνηθης χρήση τους είναι στις "μαζικές" batchιές, να γραφτούν πολλά rows "με τη μια" στη βάση. Το κάθε πεδίο του πίνακα στον οποίο θα κάνουμε για παράδειγμα insert θα έχει και ένα array. Δέκα πεδία έχει ο πίνακας? Δέκα arrays θα φτιάξουμε, αυτό βέβαια αν το απαιτεί και η σχεδίαση μας. Αν ένα πεδίο έχει σταθερά μια τιμή σε όλα τα records που θα κάνουμε insert (π.χ timestamp) ε εκεί δε χρειάζεται array.

To array insert είναι απλό. Αφού γεμίσουμε τα arrays χρησιμοποιούμε την forall δηλώνοντας πάντα τα bounds του array και τελειώσαμε. Το θέμα είναι τι γίνεται αν κάποιο από αυτά τα records που κάνουμε insert δημιουργήσει exception για παράδειγμα κάποιο duplicate.

Πως ερευνούμε πιο από τα 50000 records που μόλις προσπαθήσαμε να κάνουμε insert έσκασε? Εδώ έρχεται να σώσει την κατάσταση το SQL%BULK_EXCEPTIONS και το save exceptions του forall.

To SQL%BULK_EXCEPTIONS περιλαμβάνει ένα array το οποίο έχει indexes στα rows που αποτύχανε. Αν για παράδειγμα κάνουμε τρία inserts και αποτύχει ΜΟΝΟ το δεύτερο, τότε το SQL%BULK_EXCEPTIONS(1).error_index θα έχει τον αριθμό 2.
Φυσικά δεν θα υπάρχει SQL%BULK_EXCEPTIONS(2).error_index :)

Ένα παράδειγμα χρήσης arrays και saved exceptions είναι το παρακάτω:

Για αρχή πρέπει να φτιάξουμε μερικά types

type typ_dup_record_sns is table of test_table._sn%type index by binary_integer;
type tableOfNumbers is table of number index by binary_integer;
v_dup_recs_arr typ_dup_record_sns;



procedure theFastBatch( in_sn in tableOfNumbers
, in_name in tableOfNumbers
, in_surname in tableOfNumbers
) is

forall_error EXCEPTION;
PRAGMA exception_init(forall_error, -24381);
v_dup_recs_arr
begin
forall i in in_name.first..in_name.last save exceptions
insert into test_table
( sn
, name
, surname
) values (
in_sn(i)
, in_name(i)
, in_surname(i)
);
logger.info('End batch step 1');
exception
when forall_error then
logger.info('forall exception raised');
--insert into v_dup_recs_arr array all problematic record_sns
for i in 1..SQL%BULK_EXCEPTIONS.count loop
v_dup_recs_arr(i) := SQL%BULK_EXCEPTIONS(i).error_index ;
logger.info ('Record #'||SQL%BULK_EXCEPTIONS(i).error_index ||' Error code=['||SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE)||']');
end loop;
--Do what ever you want with the duplicates now. For example we can print them to the console
logger.info('Duplicates found!:');
for v_dup_recs_idx in v_dup_recs_arr.first.. v_dup_recs_arr.last loop
logger.info('['||in_sn(v_dup_recs_idx)||'] '||in_surname(v_dup_recs_idx)||' '||in_name(v_dup_recs_idx));
end loop;
logger.info('.-');
end;

Read more ...

Sunday, August 9, 2009

iPhone Coding Part1

Από καιρό είχα σκοπό τη δημιουργία ενός παιχνιδιού καθαρά για να γνωρίσω το μαγικό κόσμο του iPhone. Μπορεί να μην είχα το τηλέφωνο αλλά είχα κατεβάσει το Xcode μαζί με το SDK και ότι PDF χρειαζόμουν από το Apple Developer Connection. Μετά από μερικές μέρες μόρφωσης και παραμόρφωσης μπροστά στον PDF Viewer αποφάσισα ότι το πρώτο project θα έπρεπε να είναι ένα απλό retroπαιχνίδι τύπου Deflector.

Ο σκοπός του παιχνιδιού είναι να κατευθύνει ο παίχτης μια ακτίνα με τη βοήθεια κάποιων καθρεπτών στους οποίους ανακλάται. Διάφορα εμπόδια εμποδίζουν την ακτίνα να βρει το στόχο της, κάποιο πρίσμα που την σπάει σε δύο άλλες με διαφορετικά ίσως χρώματα, κάποιος τηλεμεταφορέας που τη μεταφέρει αλλού κ.α
Το πρώτο πράγμα που έκανα ήταν να στήσω ένα svn ώστε να μη χάσω τη μπάλα. Η διαδικασία πολύ απλή, για όσους δυσκολεύονται υπάρχουν μερικές χιλιάδες sites στο internet που τα λένε καλύτερα από μένα.

Το Project που επέλεξα στον αρχικό wizard στο Xcode ήταν το πιο απλό, αφού δε χρησιμοποιούμε πρακτικά τίποτα από αυτά που μας προσφέρει εκεί. Εμείς ένα παραθυράκι χρειαζόμαστε.

Ένα παιχνίδι χρειάζεται γραφικά τα οποία για μένα είναι ο μεγαλύτερος πονοκέφαλος.
Ξεκίνησα να φτιάχνω με το gimp και άλλα προγράμματα μερικά PNGs και φυσικά μιας και είμαι εντελώς άχρηστος σε οτιδήποτε έχει να κάνει με γραφιστικά, επόμενο ήταν και το αποτέλεσμα να είναι χάλια αλλά δε πτοούμαστε συνεχίζουμε. Στο κάτω-κάτω αν σκοπεύετε να φτιάξετε κάποιο παιχνίδι και να το ανεβάσετε στο appstore θα έχετε και γραφικά κάποιου επιπέδου, αλλιώς “που πα ρε Καραμήτρο?”


Μετά έφτιαξα τη δομή των groups κάτω από το resources node του Xcode. Για την ακρίβεια πρόσθεσα ένα Gfx και μέσα σε αυτό TileBackgrounds και Spites.

Νομίζω ότι είναι ευνόητο το πιο πάει που.
Επίσης τα κόκκινα που βλέπετε είναι η διαφορά του repository με το filesystem.
Περισσότερα για αυτό αργότερα.
Αφού με το καλό ξεπεράσουμε αυτό το μαρτύριο, βάσανο που λέγεται γραφιστική τέχνη πάμε ντουγρού στο ζουμί της υπόθεσης.
Delegates
Ωραίο πράγμα οι delegatoκλασες. Φανταστείτε ότι είναι βοηθητικά αντικείμενα, υπάρχουν εκεί για να κάνουν τη ζωή μας πιο εύκολη. Στο μικρό αυτό projectάκι θα τις χρησιμοποιήσουμε 2-3 φορές. Τουλάχιστον μια φορά είμαστε όλοι υποχρεωμένοι να τις χρησιμοποιήσουμε και αυτή είναι στο app delegate του κάθε προγράμματος μας.
Ας δούμε όμως τι είναι αυτό.
Η Objective C όπως και όλες οι C έχει μια main ρουτινούλα που είναι το entry point της εφαρμογής. Και εδώ έχουμε κάτι τέτοιο απλά δε χρειάζεται να το πειράξουμε. Εμείς σαν entry point σαν main δηλαδή θα έχουμε το app delegate που μας έχει φτιάξει το Xcode και έχει δώσει το όνομα της εφαρμογής μας.
Αν το πρόγραμμα το ονομάσατε deflector όπως εγώ τότε η delicateόκλαση θα λέγεται DeflectorAppDelegate και θα τη βρείτε μέσα στο classes group σε δύο αρχεία (header, body).
Το Xcode προσπαθεί να μας δομήσει λίγο τον κώδικα μας σε header files καλό είναι να ακολουθούμε αυτά τα πρότυπα. Θα μπορούσαμε να τα βάζαμε όλα σε ένα αρχείο και να μην κάναμε include τίποτα αλλά έτσι θα γράφατε χειρότερα....και από μένα!
ΚΑΙ ΚΑΝΕΝΑΣ ΔΕ ΘΑ ΜΟΥ ΠΑΡΕΙ ΤΗ ΔΟΞΑ!
Read more ...

Friday, April 3, 2009

Jesper Kid

Ένας από τους αγαπημένους μουσικούς.
Έχω κρατήσει το όνομα του στο μυαλό μου από το 1991 όταν τότε στους Silents/Crionics κυκλοφόρησε το Hardwired.



Εκπληκτική η μουσική, βασικά όλο το Demo ήταν απίστευτο, ένας "The Spy" στα καλύτερα του αλλά ειδικά η μουσική ήταν πραγματικά εμπνευσμένη.
O μικρός Kid δεν εξαφανίστηκε αλλά προχώρησε ένα βήμα παραπέρα και αργότερα εγραψε τη μουσική του Hittman και του Assasin's Creed.




Read more ...