Jump to content
Sign in to follow this  
Mu0n

Animation - mock game company intro logo made in C

Recommended Posts

Here's a mock company animated logo intro I made back in 2005 when I had goals of making full fledged games targeting System 6 Macs. It was programmed in C and used a cool little dissolve effect + fast blit non-flickering animation. It was not easy learning what was necessary for game programming in that era. It should be updated to 35 Years Ago Entertainment now, lol (or 40).

 

 

 

Share this post


Link to post
Share on other sites

That’s nice!   I did fades for games in the Mac II era by messing with the gamma table, didn’t require any fancy bitmap work. How did you do the fast dissolve with a bitmap?

Share this post


Link to post
Share on other sites

I'm fairly certain I found a tech note or a method in a book somewhere about it. Here's the method from my source code:

 

short DissolveBox(GrafPtr newImage, GrafPtr destGrafPtr, Rect *source, Rect *dest)
{
	long		offRowBytes, sizeOfOff;
	Ptr			myBits;
	Rect		bRect;
	GrafPort	myGrafPort;
	GrafPtr		myGrafPtr;
	RgnHandle	oldClipRgn;
	int			i, j;
	Pattern		thePattern;
	char		order[16];
	long		randtemp;
	char		ordertemp;
	
	/* the dissolve effect works by creating a random set of patterns which sum
	(OR) to a black pattern.  We make another offscreen bitmap and fill it with
	the pattern at each stage, and use it as a mask for the bitcopy.  Here, we
	create the offscreen bitmap. */
	
	SetPort(newImage);
	bRect = *source;
	myGrafPtr = &myGrafPort;
	OpenPort(myGrafPtr);
	offRowBytes = (((source->right - source->left) + 15) >> 4) << 1;
	sizeOfOff = (long)(source->bottom - source->top) * offRowBytes;
	OffsetRect(&bRect, -bRect.left, -bRect.top);
	myBits = NewPtr(sizeOfOff);
	if(myBits == 0L)
		return -1;		/* memory error */
	myGrafPort.portBits.baseAddr = myBits;
	myGrafPort.portBits.rowBytes = offRowBytes;
	myGrafPort.portBits.bounds = bRect;
	myGrafPort.portRect = bRect;
	oldClipRgn = myGrafPort.clipRgn;
	myGrafPort.clipRgn = destGrafPtr->visRgn;
	
	for(i = 0; i < 16; i++)
		order[i] = i;
	
	/* this randomly shuffles the order in which the pattern bits will appear */
	for(i = 15; i >= 0; i--) {
		randtemp = (((long)Random() + 32767) * (i + 1)) / 65535;
		ordertemp = order[randtemp];
		order[randtemp] = order[i];
		order[i] = ordertemp;
	}
	
	
	for(i = 0; i <= 16; i++)
	{
		StartTiming();
		SetPort(myGrafPtr);
		
		for(j = 0; j < 8; j++) thePattern[j] = 0;
		thePattern[order[i] >> 2] = 1 << (order[i] & 0x03);
		thePattern[order[i] >> 2] |= 16 << (order[i] & 0x03);
		thePattern[(order[i] >> 2) + 4] = 1 << (order[i] & 0x03);
		thePattern[(order[i] >> 2) + 4] |= 16 << (order[i] & 0x03);
        i++;

		thePattern[order[i] >> 2] = 1 << (order[i] & 0x03);
		thePattern[order[i] >> 2] |= 16 << (order[i] & 0x03);
		thePattern[(order[i] >> 2) + 4] = 1 << (order[i] & 0x03);
		thePattern[(order[i] >> 2) + 4] |= 16 << (order[i] & 0x03);
		
		
		FillRect(&bRect, thePattern);
		
		SetPort(destGrafPtr);
		
		CopyMask(&(newImage->portBits), &(myGrafPtr->portBits),
				&(destGrafPtr->portBits), source, &bRect, dest);
		TimeCorrection(CorrectTime);

	}


			StartTiming();
		SetPort(myGrafPtr);
		
		for(j = 0; j < 8; j++) thePattern[j] = 0xFF;
				FillRect(&bRect, thePattern);
		
		SetPort(destGrafPtr);
		
		CopyMask(&(newImage->portBits), &(myGrafPtr->portBits),
				&(destGrafPtr->portBits), source, &bRect, dest);
		TimeCorrection(CorrectTime);

		
	myGrafPort.clipRgn = oldClipRgn;
	ClosePort(myGrafPtr);
	DisposPtr(myBits);
	
	return 0;
}

 

Share this post


Link to post
Share on other sites

@Mu0n what do StartTiming() and TimeCorrection() do?  Do those somehow keep the CopyMask() calls in sync with vertical blanking interrupts or ... ?

Edited by Crutch

Share this post


Link to post
Share on other sites
1 hour ago, Crutch said:

@Mu0n what do StartTiming() and TimeCorrection() do?  Do those somehow keep the CopyMask() calls in sync with vertical blanking interrupts or ... ?

Here's the full source code of a simpler project that only dissolves in the moon landscape into view.

Disclaimer:

-I was far less skilled in C back then

-I had too-long-method-syndrome

-Some of those self-made includes are generic and were used in a lot of various tests, so they are a bit on the beefy side

 

StartTiming just keeps track of the tick count (into a global) of an operation I want to do:

void StartTiming(void)
{
	oldTicks=TickCount();
}

TimeCorrection forces a delay for that operation to last a predetermined time:

void TimeCorrection(int tickCount)
{
	long		newTicks;
	
	newTicks=TickCount();
	while (newTicks-oldTicks<tickCount)
		Delay(1L, &newTicks);
}

 

afasterdissolve.c

Dissolve wipe.c

Misc.c

Misc.h

Offscreen.c

Offscreen.h

Sprites.c

Sprites.h

Share this post


Link to post
Share on other sites

Yeah, it forces a delay between each frame. I wouldn't bet much money on it being accurate under every machine, but it tries to.

 

As for the original question about avoiding vertical blanking interactions - I can't seem to easily find if that project does it. I seem to have made a sprite object and abundantly use my BlitSprite method in Sprites.c, dealing with 3 cases: where the sprite must clean after itself, where there's a mask, or where it doesn't care about what damage it does under itself (white backg). Ultimately, the main Macintosh method I'm using is CopyBits from an offscreen buffer. It seems quick enough to not create flickering. I remember I spent a LOT of time settling on that after a mountain of debugging, searching tech notes and testing many strategies.

 

 

void BlitSprite(SpriteHandle theSprite, short frameIndex){
	BitMap buffBM;
	BitMapHandle buffh = NULL;
	buffBM.baseAddr = NULL;
	
	if(IsSpriteCleaning(theSprite))
		{
		buffh = (**theSprite).buffer;
		if(NULL == (**buffh).baseAddr)
			{
			buffBM.bounds = (*((**theSprite).frames))[frameIndex].bounds;
			InitBitMap(&buffBM);
			(**buffh) = buffBM;
			(**theSprite).bufferRect = (**theSprite).theRect;
			GetSpriteBuffer(theSprite);
			}
			
		HLock((Handle)buffh);
		HLock((Handle)theSprite);
		CopyBits(&(**buffh),&qd.thePort->portBits,&(**buffh).bounds,
				 &(**theSprite).bufferRect,srcCopy, NULL);
		HUnlock((Handle)theSprite);
		HUnlock((Handle)buffh);
		(**theSprite).bufferRect = (**theSprite).theRect;
		GetSpriteBuffer(theSprite);
		}
	if(IsSpriteMasked(theSprite)) 
		{
		DrawMask(theSprite,frameIndex,srcBic);
		DrawSprite(theSprite,frameIndex,srcOr);
		}
	else DrawSprite(theSprite,frameIndex,srcCopy);
}

 

Edited by Mu0n

Share this post


Link to post
Share on other sites
On 6/11/2019 at 12:05 PM, olePigeon said:

Very cool.  I wish I had the mental aptitude for programming.  I have a lot of ideas, but no way to realize them. :(

You are not alone. I really wish I could do this...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×